[AAI-80 Amsterdam] checking in source code 47/6347/1
authorMuller, Andrew (am8383) <am8383@ems.att.com>
Fri, 28 Jul 2017 20:15:06 +0000 (16:15 -0400)
committerMuller, Andrew (am8383) <am8383@us.att.com>
Fri, 28 Jul 2017 20:32:08 +0000 (16:32 -0400)
Change-Id: Ic041f9bca773ec6870b7e29d7dd02fa03dd2947b
Signed-off-by: Muller, Andrew (am8383) <am8383@us.att.com>
132 files changed:
.gitignore [new file with mode: 0644]
License.txt [new file with mode: 0644]
README.md [new file with mode: 0644]
pom.xml [new file with mode: 0755]
src/main/java/org/openecomp/aai/champ/ChampAPI.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/ChampCapabilities.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/ChampGraph.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/event/AbstractLoggingChampGraph.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/event/ChampEvent.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/exceptions/ChampIndexNotExistsException.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/exceptions/ChampMarshallingException.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/exceptions/ChampObjectNotExistsException.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/exceptions/ChampRelationshipNotExistsException.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/exceptions/ChampSchemaViolationException.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/exceptions/ChampUnmarshallingException.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/graph/impl/AbstractGremlinChampGraph.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/graph/impl/AbstractTinkerpopChampGraph.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/graph/impl/AbstractValidatingChampGraph.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/graph/impl/ChampAPIImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/graph/impl/InMemoryChampGraphImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/graph/impl/TitanChampGraphImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/ie/Exporter.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/ie/GraphMLImporterExporter.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/ie/Importer.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/ChampCardinality.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/ChampConnectionConstraint.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/ChampConnectionMultiplicity.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/ChampElement.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/ChampField.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/ChampObject.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/ChampObjectConstraint.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/ChampObjectIndex.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/ChampPartition.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/ChampPropertyConstraint.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/ChampRelationship.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/ChampRelationshipConstraint.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/ChampRelationshipIndex.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/ChampSchema.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/BuildStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/KeyStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/PropertiesStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/index/CreateObjectIndexable.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/index/CreateRelationshipIndexable.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/index/ObjectIndexFieldStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/index/ObjectIndexTypeStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/index/RelationshipIndexFieldStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/index/RelationshipIndexTypeStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/index/impl/CreateObjectIndexableImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/index/impl/CreateRelationshipIndexableImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/object/CreateChampObjectable.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectBuildOrPropertiesStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectBuildOrPropertiesStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectKeyStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/object/impl/CreateChampObjectableImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/partition/CreateChampPartitionable.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/partition/impl/CreateChampPartionableImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/CreateChampRelationshipable.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/RelationshipBuildOrPropertiesStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceBuildOrPropertiesStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceFromStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceKeyStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceTypeOrFromStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceTypeStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetBuildOrPropertiesStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetFromStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetKeyStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetTypeOrFromStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetTypeStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/ChampRelationshipKeyStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/CreateChampRelationshipableImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/RelationshipBuildOrPropertiesStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceBuildOrPropertiesStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceKeyStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceTypeOrFromStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetBuildOrPropertiesStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetKeyStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetTypeOrFromStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/CreateChampSchemable.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintBuildStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintFieldStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintFieldTypeStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintPropertyStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintRequiredOptionalStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintSubStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintTypeStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintBuildStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintFieldStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintFieldTypeStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintMultiplicityStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintPropertyOptionalsStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintRequiredOptionalStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintSourceStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintSubStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintTargetStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintTypeStep.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/CreateChampSchemableImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintBuildStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintFieldStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintPropertyStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintSubStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintBuildStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintPropertyOptionalsStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintRequiredOptionalStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintSubStepImpl.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/perf/ChampAPIPerformanceTest.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/schema/AlwaysValidChampSchemaEnforcer.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/schema/ChampSchemaEnforcer.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/schema/DefaultChampSchemaEnforcer.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/transform/Champformer.java [new file with mode: 0644]
src/main/java/org/openecomp/aai/champ/transform/TinkerpopChampformer.java [new file with mode: 0644]
src/test/java/org/openecomp/aai/champ/concurrency/ConcurrencyTest.java [new file with mode: 0644]
src/test/java/org/openecomp/aai/champ/core/BaseChampAPITest.java [new file with mode: 0644]
src/test/java/org/openecomp/aai/champ/core/ChampAPITest.java [new file with mode: 0644]
src/test/java/org/openecomp/aai/champ/core/ChampElementTest.java [new file with mode: 0644]
src/test/java/org/openecomp/aai/champ/core/ChampFieldTest.java [new file with mode: 0644]
src/test/java/org/openecomp/aai/champ/core/ChampObjectIndexTest.java [new file with mode: 0644]
src/test/java/org/openecomp/aai/champ/core/ChampObjectTest.java [new file with mode: 0644]
src/test/java/org/openecomp/aai/champ/core/ChampPartitionTest.java [new file with mode: 0644]
src/test/java/org/openecomp/aai/champ/core/ChampPropertyConstraintTest.java [new file with mode: 0644]
src/test/java/org/openecomp/aai/champ/core/ChampRelationshipIndexTest.java [new file with mode: 0644]
src/test/java/org/openecomp/aai/champ/core/ChampRelationshipTest.java [new file with mode: 0644]
src/test/java/org/openecomp/aai/champ/core/ChampSchemaTest.java [new file with mode: 0644]
src/test/java/org/openecomp/aai/champ/event/AbstractLoggingChampGraphTest.java [new file with mode: 0644]
src/test/java/org/openecomp/aai/champ/exceptions/ChampExceptionTest.java [new file with mode: 0644]
src/test/java/org/openecomp/aai/champ/ie/ExportTest.java [new file with mode: 0644]
src/test/java/org/openecomp/aai/champ/ie/ImportTest.java [new file with mode: 0644]
src/test/resources/import-test.graphml [new file with mode: 0755]
src/test/resources/logback.xml [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..9f1f7a5
--- /dev/null
@@ -0,0 +1,5 @@
+.classpath
+.project
+.settings/
+target/
+jacoco.exec
diff --git a/License.txt b/License.txt
new file mode 100644 (file)
index 0000000..469f362
--- /dev/null
@@ -0,0 +1,21 @@
+============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.
+
+
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..79700f3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,400 @@
+Champ
+=====
+
+What is Champ?
+--------------
+
+Champ is an abstraction from underlying graph storage systems that A&AI would otherwise interface with.
+
+Building Champ
+--------------
+
+Good ol' 'mvn clean install' does the trick.
+
+API Specification
+-----------------
+
+Champ has CRUD methods for:
+
+1) Objects
+2) Relationships
+3) Partitions (subgraphs)
+3) Indices (on object and relationship properties)
+4) Schemas
+
+For each of these types, we offer builders and a more user-friendly fluent interface.
+
+In the future we plan on adding in traversals, but at the moment that is outside the scope of Champ.  If you have suggestions on how this should be implemented, we look forward to your pull request.
+
+API Implementations
+-------------------
+
+Champ ships with a simple in-memory implementation as well as a Titan implementation.  We recommend the in-memory implementation for unit testing and prototyping.  If you would like to have your implementation referenced in this readme, please create a pull-request.  Please note that all implementations will reside in their own repository - not in the Champ repository.
+
+Usage
+-----
+
+### ChampAPI
+
+The ChampAPI interface is basically just for tracking and properly shutting down multiple graph instances.  If you need this functionality, use the ChampAPI.  However, if you only ever access 1 graph, you may choose to use a single ChampGraph.
+
+For getting started quickly, use the ChampAPI.Factory or ChampGraph.Factory to create either an In-memory implementation (for dev/test) or if you're running Titan locally, you can start a Titan instance.  For complex environments, each ChampGraph implementation will vary - the Titan implementation is described below.
+
+### ChampGraph
+
+This is the meat and potatoes of Champ.  It contains all of the CRUD methods mentioned above for Objects, Relationships, Partitions, Indices, and Schemas. Each implementation varies in how you instantiate it. The ones that ship with Champ are described below.
+
+#### In-memory ChampGraph
+
+Simply:
+
+```
+final ChampGraph graph = ChampGraph.Factory.newInstance(ChampGraph.Type.IN_MEMORY, "someGraphName");
+
+//Do stuff with graph
+
+graph.shutdown();
+
+```
+
+or:
+
+```
+final ChampAPI api = ChampAPI.Factory.newInstance(ChampGraph.Type.IN_MEMORY);
+final ChampGraph dogsGraph = api.getGraph("dogsGraph");
+final ChampGraph catsGraph = api.getGraph("catsGraph");
+
+api.shutdown(); //This will shutdown all graphs started by api.getGraph(String)
+
+```
+
+#### Titan ChampGraph
+
+For a Titan instance running on top of Cassandra locally, simply:
+
+```
+final ChampGraph graph = ChampGraph.Factory.newInstance(ChampGraph.Type.TITAN, "dogsGraph");
+
+//Do stuff with graph
+
+graph.shutdown();
+
+```
+or:
+
+```
+final ChampAPI api = ChampAPI.Factory.newInstance(ChampGraph.Type.TITAN);
+final ChampGraph dogsGraph = api.getGraph("dogsGraph");
+final ChampGraph cats Graph = api.getGraph("catsGraph");
+
+api.shutdown(); //This will shutdown all graph started by api.getGraph(String);
+
+```
+
+For more complex/customized configurations:
+
+```
+       final ChampGraph graph = new TitanChampGraphImpl.Builder(graphName)
+                                                       .property("storage.backend", "cassandrathrift")
+                                                       .property("storage.hostname", "localhost")
+                                                       .build();
+```
+
+The calls to .property(String, String) accept all configuration options found [here](http://s3.thinkaurelius.com/docs/titan/1.0.0/titan-config-ref.html)
+
+You could also implement the ChampAPI interface to manage multiple graphs connected to this Titan cluster.  See the ChampAPIImpl class for an example of how to do this.
+
+### Creating Objects
+
+#### Create a new object
+
+```
+ChampObject.create()
+          .ofType("foo")  //The "foo" type of object can be constrained by a ChampObjectConstraint
+          .withoutKey()   //No key for this object indicates that the underlying Champ implementation should create this object
+          .withProperty("bar", "string") //Zero or more properties can be set on a ChampObject
+          .withProperty("baz", 30)
+          .build()
+```
+
+#### Copy an existing object
+
+```
+ChampObject.create()
+          .from(foo) //'foo' is a reference to a ChampObject
+          .withoutKey()
+          .withProperty("pi", 3.14f)
+          .build()
+```
+
+#### Persisting an object
+
+```
+final ChampGraph graph = ChampGraph.Factory.newInstance(graphType, "neighborhoodDogsGraph");
+graph.storeObject(foo); //'foo' is a reference to a ChampObject
+graph.shutdown(); //The ChampGraph is thread-safe, and only one needs to be created
+               //Once your application is finished using it, call shutdown()
+               //to cleanup any loose ends
+```
+
+#### Retrieve an object
+
+```
+final ChampGraph graph = ChampGraph.Factory.newInstance(graphType, "neighborhoodDogsGraph");
+final Optional<ChampObject> object = graph.retrieveObject("329847"); //Note that the key is usually only known by virtue of previously storing/retrieving/querying it
+
+graph.shutdown();
+
+```
+
+#### Query objects
+
+```
+final ChampGraph graph = ChampGraph.Factory.newInstance(graphType, "neighborhoodDogsGraph");
+final Stream<ChampObject> objects = graph.queryObjects(Collection.singletonMap("favoriteDog", "Ace"));
+
+objects.close(); //You must close the stream when you are finished with it
+graph.shutdown();
+
+```
+
+### Creating Relationships
+
+#### Create a new relationship
+
+In this example we create the relationship:
+
+dog eats dogPellets
+
+```
+ChampRelationship.create()
+                .ofType("eats")
+                .withoutKey()
+                .withSource()
+                       .ofType("dog")
+                       .withoutKey()
+                       .withProperty("name", "champ")
+                       .build()
+                .withTarget()
+                       .ofType("dogPellets")
+                       .withoutKey()
+                       .withProperty("brand", "costco")
+                       .build()
+                .withProperty("at", System.currentTimeMillis())
+                .build()
+```
+
+#### Persisting a relationship
+
+```
+final ChampGraph graph = ChampGraph.Factory.newInstance(graphType, "neighborhoodDogsGraph");
+graph.storeRelationship(champEatsCostcoFood); //'champEatsCostcoFood' is a reference to a ChampRelationship
+graph.shutdown(); //The ChampGraph is thread-safe, and only one needs to be created
+               //Once your application is finished using it, call shutdown()
+               //to cleanup any loose ends
+```
+
+#### Retrieving incident relationships
+
+```
+final ChampGraph graph = ChampGraph.Factory.newInstance(graphType, "neighborhoodDogsGraph");
+final Stream<ChampRelationship> relationships = graph.retrieveRelationships(ChampObject.create().withKey("foo").build());
+
+relationships.close(); //You must close the stream when you are done with it
+graph.shutdown(); //The ChampGraph is thread-safe, and only one needs to be created
+
+```
+
+#### Querying relationship
+
+```
+final ChampGraph graph = ChampGraph.Factory.newInstance(graphType, "neighborhoodDogsGraph");
+final Stream<ChampRelationship> relationships = graph.queryRelationships(Collections.singletonMap("favoriteHydrant", 42);
+
+relationships.close(); //You must close the stream when you are done with it
+graph.shutdown(); //The ChampGraph is thread-safe, and only one needs to be created
+
+```
+
+### Creating partitions
+#### Create a new partition
+
+Champ partitions are subgraphs (i.e. a collection of objects and relationships)
+** Note: We are still in the proces of creating a fluent API for partitions **
+
+
+```
+ChampPartition.create()
+             .withObject(
+                       ChampObject.create()
+                                  .ofType("dog")
+                                  .withoutKey()
+                                  .build()
+             )
+             .withObject(
+                       ChampObject.create()
+                                  .ofType("cat")
+                                  .withoutKey()
+                                  .build()
+             .withObject(
+                       ChampObject.create()
+                                  .ofType("bird")
+                                  .withoutKey()
+                                  .build()
+             )
+             .withRelationship(
+                       ChampRelationship.create()
+                                               ...
+                                        .build()
+             )
+             .build()
+```
+
+#### Persisting a partition
+
+```
+final ChampGraph graph = ChampGraph.Factory.newInstance(graphType, "neighborhoodDogsGraph");
+graph.storePartition(dogsOnMyBlock); //'dogsOnMyBlock' is a reference to a ChampPartition
+graph.shutdown(); //The ChampGraph is thread-safe, and only one needs to be created
+               //Once your application is finished using it, call shutdown()
+               //to cleanup any loose ends
+```
+
+### Creating indices
+#### Create an object index
+
+```
+ChampObjectIndex.create()
+               .ofName("dogName")
+               .onType("dog")
+               .forField("name")
+               .build()
+```
+
+#### Create a relationship index
+
+```
+ChampRelationshipIndex.create()
+                     .ofName("eatsAtTime")
+                     .onType("eats")
+                     .forField("at")
+                     .build()
+```
+
+#### Persisting an object index
+
+```
+final ChampGraph graph = ChampGraph.Factory.newInstance(graphType, "neighborhoodDogsGraph");
+graph.storeObjectIndex(dogName); //'dogName' is a reference to a ChampObjectIndex
+graph.shutdown(); //The ChampGraph is thread-safe, and only one needs to be created
+               //Once your application is finished using it, call shutdown()
+               //to cleanup any loose ends
+```
+
+#### Persisting a relationship index
+
+```
+final ChampGraph graph = ChampGraph.Factory.newInstance(graphType, "neighborhoodDogsGraph");
+graph.storeRelationshipIndex(eatsAtTime); //'eatsAtTime' is a reference to a ChampObjectIndex
+graph.shutdown(); //The ChampGraph is thread-safe, and only one needs to be created
+               //Once your application is finished using it, call shutdown()
+               //to cleanup any loose ends
+```
+#### Retrieving an object index
+
+```
+final ChampGraph graph = ChampGraph.Factory.newInstance(graphType, "neighborhoodDogsGraph");
+graph.retrieveObjectIndex("dogName");
+graph.shutdown();
+
+```
+
+#### Retrieving a relationship index
+
+```
+final ChampGraph graph = ChampGraph.Factory.newInstance(graphType, "neighborhoodDogsGraph");
+graph.retrieveRelationshipIndex("eatsAtTime");
+graph.shutdown();
+
+```
+
+### Creating schemas
+#### Create a schema
+
+The following schema restricts objects of type foo to have a required property "property1" as an Integer, and optional property "property2" as a String (Strings are the default type for object properties).  It also restricts relationships of type bar to only be allowed to originate from the object type foo.
+
+```
+ChampSchema.create()
+       .withObjectConstraint()
+               .onType("foo")
+               .withPropertyConstraint()
+                       .onField("property1")
+                       .ofType(Integer.class)
+                       .required()
+                       .build()
+               .withPropertyConstraint()
+                       .onField("property2")
+                       .optional()
+                       .build()
+               .build()
+       .withRelationshipConstraint()
+               .onType("bar")
+               .withPropertyConstraint()
+                       .onField("at")
+                       .ofType(String.class)
+                       .optional()
+                       .build()
+               .withConnectionConstraint()
+                       .sourcedFrom("foo")
+                       .targetToAny()
+                       .build()
+               .build()
+       .build();
+```
+
+#### Persisting a schema
+
+```
+final ChampGraph graph = ChampGraph.Factory.newInstance(graphType, "neighborhoodDogsGraph");
+graph.storeSchema(neighborhoodDogsSchema); //'neighborhoodDogsSchema' is a reference to a ChampObjectIndex
+graph.updateSchema(neighborhoodDogConstraint); //'neighborhoosDogConstraint' is a reference to a ChampObjectConstraint
+graph.updateSchema(eatsAtConstraint); //'eatsAtConstraint' is a reference to a ChampRelationshipIndex
+graph.shutdown(); //The ChampGraph is thread-safe, and only one needs to be created
+       //Once your application is finished using it, call shutdown()
+       //to cleanup any loose ends
+```
+
+#### Retrieving a schema
+
+```
+final ChampGraph graph = ChampGraph.Factory.newInstance(graphType, "neighborhoodDogsGraph");
+
+final ChampSchema schema = graph.retrieveSchema();
+
+graph.shutdown();
+
+```
+
+### Champ Performance Testing
+
+There is a jar-with-dependencies provided in maven that contains a performance test you can move around and get some idea of how well Champ is running on a cluster of your choice.  At the moment, the test only runs against a Titan implementation.
+
+#### Example running an in-memory test
+
+```
+
+java -cp champ-0.0.1-SNAPSHOT-jar-with-dependencies.jar org.openecomp.aai.champ.perf.ChampAPIPerformanceTest --champ.graph.type=IN_MEMORY
+
+
+```
+
+#### Example running a Titan test
+
+Note that after the --champ.graph.type=TITAN parameter is provided, you may provide any configuration that is specified by Titan (see link above for the documentation)
+
+```
+
+java -cp champ-0.0.1-SNAPSHOT-jar-with-dependencies.jar org.openecomp.aai.champ.perf.ChampAPIPerformanceTest --champ.graph.type=TITAN --storage.backend=cassandrathrift --storage.hostname=localhost
+
+
+```
diff --git a/pom.xml b/pom.xml
new file mode 100755 (executable)
index 0000000..15df258
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,403 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <groupId>org.openecomp.aai</groupId>
+       <artifactId>champ</artifactId>
+       <version>1.1.0-SNAPSHOT</version>
+       <properties>
+               <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+               <tinkerpop.version>3.0.1-incubating</tinkerpop.version>
+               <nexusproxy>https://nexus.onap.org/</nexusproxy>
+               <sitePath>/content/sites/site/org/onap/aai/${project.artifactId}/${project.version}</sitePath>
+               <sonar.language>java</sonar.language>
+               <sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
+               <sonar.surefire.reportsPath>${project.build.directory}/surefire-reports</sonar.surefire.reportsPath>
+               <sonar.jacoco.reportPath>${project.build.directory}/jacoco.exec</sonar.jacoco.reportPath>
+               <sonar.jacoco.reportMissing.force.zero>false</sonar.jacoco.reportMissing.force.zero>
+               <sonar.projectVersion>${project.version}</sonar.projectVersion>
+       </properties>
+       <distributionManagement>
+               <repository>
+                       <id>ecomp-releases</id>
+                       <name>ECOMP Release Repository</name>
+                       <url>${nexusproxy}/content/repositories/releases/</url>
+               </repository>
+               <snapshotRepository>
+                       <id>ecomp-snapshots</id>
+                       <name>ECOMP Snapshot Repository</name>
+                       <url>${nexusproxy}/content/repositories/snapshots/</url>
+               </snapshotRepository>
+               <site>
+                       <id>ecomp-site</id>
+                       <url>dav:${nexusproxy}${sitePath}</url>
+               </site>
+       </distributionManagement>
+       <dependencies>
+               <dependency>
+                       <groupId>junit</groupId>
+                       <artifactId>junit</artifactId>
+                       <version>4.12</version>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>ch.qos.logback</groupId>
+                       <artifactId>logback-classic</artifactId>
+                       <version>1.2.1</version>
+                       <optional>true</optional>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.tinkerpop</groupId>
+                       <artifactId>tinkergraph-gremlin</artifactId> 
+                       <version>${tinkerpop.version}</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.tinkerpop</groupId>
+                       <artifactId>gremlin-core</artifactId> 
+                       <version>${tinkerpop.version}</version>
+                       <exclusions>
+                               <exclusion>
+                                       <groupId>org.slf4j</groupId>
+                                       <artifactId>slf4j-log4j12</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.slf4j</groupId>
+                                       <artifactId>jcl-over-slf4j</artifactId>
+                               </exclusion>
+                       </exclusions>
+               </dependency>
+               <dependency>
+                       <groupId>com.thinkaurelius.titan</groupId>
+                       <artifactId>titan-cassandra</artifactId>
+                       <version>1.0.0</version>
+                       <optional>true</optional>
+                       <exclusions>
+                               <exclusion>
+                                       <groupId>org.apache.tinkerpop</groupId>
+                                       <artifactId>gremlin-groovy</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.slf4j</groupId>
+                                       <artifactId>slf4j-log4j12</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>ch.qos.logback</groupId>
+                                       <artifactId>logback-classic</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.apache.tinkerpop</groupId>
+                                       <artifactId>gremlin-core</artifactId>
+                               </exclusion>
+                       </exclusions>
+               </dependency>
+               <dependency>
+                       <groupId>com.thinkaurelius.titan</groupId>
+                       <artifactId>titan-hbase</artifactId>
+                       <version>1.0.0</version>
+                       <optional>true</optional>
+                       <exclusions>
+                               <exclusion>
+                                       <groupId>org.apache.tinkerpop</groupId>
+                                       <artifactId>gremlin-groovy</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.slf4j</groupId>
+                                       <artifactId>slf4j-log4j12</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>ch.qos.logback</groupId>
+                                       <artifactId>logback-classic</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.apache.tinkerpop</groupId>
+                                       <artifactId>gremlin-core</artifactId>
+                               </exclusion>
+                       </exclusions>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.hbase</groupId>
+                       <artifactId>hbase-client</artifactId>
+                       <version>0.98.4-hadoop2</version>
+                       <exclusions>
+                               <exclusion>
+                                       <groupId>org.slf4j</groupId>
+                                       <artifactId>slf4j-log4j12</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>com.google.guava</groupId>
+                                       <artifactId>guava</artifactId>
+                               </exclusion>
+                       </exclusions>
+               </dependency>
+               <dependency>
+                       <groupId>jdk.tools</groupId>
+                       <artifactId>jdk.tools</artifactId>
+                       <version>1.8</version>
+                       <scope>system</scope>
+                       <systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
+               </dependency>
+               <dependency>
+                       <groupId>org.jacoco</groupId>
+                       <artifactId>org.jacoco.agent</artifactId>
+                       <version>0.7.9</version>
+                       <classifier>runtime</classifier>
+                       <scope>test</scope>
+               </dependency>
+
+        <!-- Event Bus Client. -->
+        <dependency>
+            <groupId>com.att.nsa</groupId>
+            <artifactId>cambriaClient</artifactId>
+            <version>0.0.1</version>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5</version>
+        </dependency>
+        
+       </dependencies>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.codehaus.mojo</groupId>
+                               <artifactId>sonar-maven-plugin</artifactId>
+                               <version>3.2</version>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.jacoco</groupId>
+                               <artifactId>jacoco-maven-plugin</artifactId>
+                               <version>0.7.9</version>
+                               <configuration>
+                                       <excludes>
+                                               <!-- These three need to be included again at some point -->
+                                               <exclude>**/AbstractGremlinChampGraph*</exclude>
+                                               <exclude>**/DseChampGraphImpl*</exclude>
+                                               <exclude>**/DseChampformer*</exclude>
+                                               <!-- Permanently excluded, not worth testing -->
+                                               <exclude>**/ChampAPIPerformanceTest*</exclude>
+                                       </excludes>
+                               </configuration>
+                               <executions>
+                                       <execution>
+                                               <id>default-prepare-agent</id>
+                                               <goals>
+                                                       <goal>prepare-agent</goal>
+                                               </goals>
+                                       </execution>
+                                       <execution>
+                                               <id>default-report</id>
+                                               <phase>prepare-package</phase>
+                                               <goals>
+                                                       <goal>report</goal>
+                                               </goals>
+                                       </execution>
+                                       <execution>
+                                               <id>default-check</id>
+                                               <goals>
+                                                       <goal>check</goal>
+                                               </goals>
+                                               <configuration>
+                                                       <rules>
+                                                               <!--  implementation is needed only for Maven 2  -->
+                                                               <rule implementation="org.jacoco.maven.RuleConfiguration">
+                                                                       <element>BUNDLE</element>
+                                                                       <limits>
+                                                                               <!--  implementation is needed only for Maven 2  -->
+                                                                               <limit implementation="org.jacoco.report.check.Limit">
+                                                                                       <counter>INSTRUCTION</counter>
+                                                                                       <value>COVEREDRATIO</value>
+                                                                                       <minimum>.75</minimum>
+                                                                               </limit>
+                                                                               <limit implementation="org.jacoco.report.check.Limit">
+                                                                                       <counter>BRANCH</counter>
+                                                                                       <value>COVEREDRATIO</value>
+                                                                                       <minimum>.74</minimum>
+                                                                               </limit>
+                                                                               <limit implementation="org.jacoco.report.check.Limit">
+                                                                                       <counter>COMPLEXITY</counter>
+                                                                                       <value>COVEREDRATIO</value>
+                                                                                       <minimum>.75</minimum>
+                                                                               </limit>
+                                                                               <limit implementation="org.jacoco.report.check.Limit">
+                                                                                       <counter>LINE</counter>
+                                                                                       <value>COVEREDRATIO</value>
+                                                                                       <minimum>.80</minimum>
+                                                                               </limit>
+                                                                               <limit implementation="org.jacoco.report.check.Limit">
+                                                                                       <counter>METHOD</counter>
+                                                                                       <value>COVEREDRATIO</value>
+                                                                                       <minimum>.90</minimum>
+                                                                               </limit>
+                                                                               <limit implementation="org.jacoco.report.check.Limit">
+                                                                                       <counter>CLASS</counter>
+                                                                                       <value>MISSEDCOUNT</value>
+                                                                                       <maximum>0</maximum>
+                                                                               </limit>
+                                                                       </limits>
+                                                               </rule>
+                                                       </rules>
+                                               </configuration>
+                                       </execution>
+                               </executions>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-assembly-plugin</artifactId>
+                               <version>3.0.0</version>
+                               <configuration>
+                                       <descriptorRefs>
+                                               <descriptorRef>jar-with-dependencies</descriptorRef>
+                                       </descriptorRefs>
+                               </configuration>
+                               <executions>
+                                       <execution>
+                                               <id>make-jar-with-dependencies</id>
+                                               <phase>package</phase>
+                                               <goals>
+                                                       <goal>single</goal>
+                                               </goals>
+                                       </execution>
+                               </executions>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                               <version>3.6.1</version>
+                               <configuration>
+                                       <source>1.8</source>
+                                       <target>1.8</target>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <artifactId>maven-release-plugin</artifactId>
+                               <version>2.4.2</version>
+                               <dependencies>
+                                       <dependency>
+                                               <groupId>org.apache.maven.scm</groupId>
+                                               <artifactId>maven-scm-provider-gitexe</artifactId>
+                                               <version>1.8.1</version>
+                                       </dependency>
+                               </dependencies>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                               <version>3.0.1</version>
+                               <executions>
+                                       <execution>
+                                               <id>attach-sources</id>
+                                               <goals>
+                                                       <goal>jar</goal>
+                                               </goals>
+                                       </execution>
+                               </executions>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-javadoc-plugin</artifactId>
+                               <version>2.9.1</version>
+                               <executions>
+                                       <execution>
+                                               <id>attach-javadocs</id>
+                                               <goals>
+                                                       <goal>jar</goal>
+                                               </goals>
+                                       </execution>
+                               </executions>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-surefire-plugin</artifactId>
+                               <version>2.12.2</version>
+                               <configuration>
+                                       <systemPropertyVariables>
+                                               <jacoco-agent.destfile>target/jacoco.exec</jacoco-agent.destfile>
+                                       </systemPropertyVariables>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-deploy-plugin</artifactId>
+                               <executions>
+                                       <execution>
+                                               <id>default-deploy</id>
+                                               <phase>none</phase>
+                                       </execution>
+                               </executions>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.sonatype.plugins</groupId>
+                               <artifactId>nexus-staging-maven-plugin</artifactId>
+                               <version>1.6.7</version>
+                               <extensions>true</extensions>
+                               <configuration>
+                                       <nexusUrl>${nexusproxy}</nexusUrl>
+                                       <stagingProfileId>176c31dfe190a</stagingProfileId>
+                                       <serverId>ecomp-staging</serverId>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-site-plugin</artifactId>
+                               <version>3.6</version>
+                               <dependencies>
+                                       <dependency>
+                                               <groupId>org.apache.maven.wagon</groupId>
+                                               <artifactId>wagon-webdav-jackrabbit</artifactId>
+                                               <version>2.10</version>
+                                       </dependency>
+                               </dependencies>
+                       </plugin>
+                           <plugin>
+                               <groupId>com.mycila</groupId>
+                               <artifactId>license-maven-plugin</artifactId>
+                               <version>3.0</version>
+                               <configuration>
+                                   <header>License.txt</header>
+                                   <includes>
+                                       <include>**/*.java</include>
+                                       <include>**/*.ksh</include>
+                                       <include>**/*.sh</include>
+                                       <include>**/*.ftl</include>
+                                       <include>**/*.xsd</include>
+                                       <include>**/*.xjb</include>
+                                       <include>**/*.yml</include>
+                                       <include>**/*.yaml</include>
+                                       <include>**/aai*.xml</include>
+                                       <include>**/*logback*.xml</include>
+                                       <include>**/*aaiconfig*.properties</include>
+                                       <include>**/*titan*.properties</include>
+                                   </includes>
+                               </configuration>
+                               <executions>
+                                   <execution>
+                                       <goals>
+                                           <goal>format</goal>
+                                       </goals>
+                                       <phase>process-sources</phase>
+                                   </execution>
+                               </executions>
+                           </plugin>
+               </plugins>
+       </build>
+       <reporting>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-javadoc-plugin</artifactId>
+                               <version>2.10.4</version>
+                               <configuration>
+                                       <failOnError>false</failOnError>
+                                       <doclet>org.umlgraph.doclet.UmlGraphDoc</doclet>
+                                       <docletArtifact>
+                                               <groupId>org.umlgraph</groupId>
+                                               <artifactId>umlgraph</artifactId>
+                                               <version>5.6</version>
+                                       </docletArtifact>
+                                       <additionalparam>-views</additionalparam>
+                                       <useStandardDocletOptions>true</useStandardDocletOptions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </reporting>
+</project>
diff --git a/src/main/java/org/openecomp/aai/champ/ChampAPI.java b/src/main/java/org/openecomp/aai/champ/ChampAPI.java
new file mode 100644 (file)
index 0000000..1eb3758
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * ============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.openecomp.aai.champ;
+
+import org.openecomp.aai.champ.graph.impl.ChampAPIImpl;
+
+public interface ChampAPI {
+
+       /**
+        * A factory for constructing basic ChampAPI implementations (minimal).
+        * If finer control is needed, you should consider accessing an implementation's
+        * constructors/builders.
+        */
+       public static final class Factory {
+               private Factory() { throw new RuntimeException("Cannot instantiate ChampAPI.Factory"); }
+
+               public static ChampAPI newInstance(ChampGraph.Type type) {
+                       return new ChampAPIImpl(type);
+               }
+       }
+
+       public ChampGraph getGraph(String graphName);
+
+       public ChampGraph.Type getType();
+
+       /**
+        * Shutdown the ChampAPI. It is up to the caller to synchronize access to the ChampAPI
+        * so that shutting it down does not interfere with concurrent operations.
+        */
+       public void shutdown();
+}
+
diff --git a/src/main/java/org/openecomp/aai/champ/ChampCapabilities.java b/src/main/java/org/openecomp/aai/champ/ChampCapabilities.java
new file mode 100644 (file)
index 0000000..31624c7
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============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.openecomp.aai.champ;
+
+public interface ChampCapabilities {
+
+       public boolean canDeleteObjectIndices();
+       public boolean canDeleteRelationshipIndices();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/ChampGraph.java b/src/main/java/org/openecomp/aai/champ/ChampGraph.java
new file mode 100644 (file)
index 0000000..20b9a17
--- /dev/null
@@ -0,0 +1,306 @@
+/**
+ * ============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.openecomp.aai.champ;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import org.openecomp.aai.champ.exceptions.ChampIndexNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.graph.impl.InMemoryChampGraphImpl;
+import org.openecomp.aai.champ.graph.impl.TitanChampGraphImpl;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+import org.openecomp.aai.champ.model.ChampSchema;
+
+public interface ChampGraph {
+
+       /**
+        * Types that the Factory is capable of constructing
+        */
+       public enum Type {
+               IN_MEMORY,
+               TITAN/*,
+               DSE //DSE is still in beta, so leave it out for now
+               */
+       }
+
+       /**
+        * A factory for constructing basic ChampAPI implementations (minimal).
+        * If finer control is needed, you should consider accessing an implementation's
+        * constructors/builders.
+        */
+       public static class Factory {
+               public static ChampGraph newInstance(ChampGraph.Type type, String graphName) {
+                       switch (type) {
+                       case IN_MEMORY:
+                               return new InMemoryChampGraphImpl.Builder().build();
+                       case TITAN:
+                               return new TitanChampGraphImpl.Builder(graphName)
+                                                                                       .property("storage.backend", "inmemory")
+                                                                                       .build();
+                       /*
+                       case DSE: //See above, DSE still in beta
+                               return new DseChampGraphImpl.Builder("localhost").build();
+                       */
+                       default:
+                               throw new RuntimeException("Unknown type of ChampAPI implementation");
+                       }
+               }
+       }
+
+       /**
+        * Create/Update an object.  If the ChampObject key is present, an update will be attempted,
+        * otherwise a create will be attempted.  Each implementation has different guarantees on
+        * validation - see the specific implementation for more details on this.
+        * @param object - The ChampObject that you wish to store in the graph
+        * @return The ChampObject as it was stored
+        * @throws ChampMarshallingException If the {@code object} is not able to be marshalled into the backend representation
+        * @throws ChampSchemaViolationException If the {@code object} violates the constraints specifed by {@link ChampGraph#retrieveSchema}
+        * @throws ChampObjectNotExistsException If {@link org.openecomp.aai.champ.model.ChampObject#getKey}.isPresent() but the object cannot be found in the graph 
+        */
+       public ChampObject storeObject(ChampObject object) throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException;
+       
+       /**
+        * Replace an object.  ChampObject key is mandatory
+        * Each implementation has different guarantees on
+        * validation - see the specific implementation for more details on this.
+        * @param object - The ChampObject that you wish to replace in the graph
+        * @return The ChampObject as it was stored
+        * @throws ChampMarshallingException If the {@code object} is not able to be marshalled into the backend representation
+        * @throws ChampSchemaViolationException If the {@code object} violates the constraints specifed by {@link ChampGraph#retrieveSchema}
+        * @throws ChampObjectNotExistsException If {@link org.openecomp.aai.champ.model.ChampObject#getKey} is not present or object not found in the graph
+        */
+       public ChampObject replaceObject(ChampObject object) throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException;
+
+       /**
+        * Retrieve an object by its key.
+        * @param key The key of the ChampObject in the graph {@link org.openecomp.aai.champ.model.ChampObject#getKey()}
+        * @return The {@link org.openecomp.aai.champ.model.ChampObject} if it was present, otherwise {@link Optional#empty()}
+        * @throws ChampUnmarshallingException If the object was found, but could not be unmarshalled
+        */
+       public Optional<ChampObject> retrieveObject(Object key) throws ChampUnmarshallingException;
+
+       /**
+        * Delete an object by its key.
+        * @param key The key of the ChampObject in the graph {@link ChampObject#getKey}
+        * @throws ChampObjectNotExistsException If the object did not exist in the graph
+        */
+       public void deleteObject(Object key) throws ChampObjectNotExistsException;
+
+       /**
+        * Retrieve all the objects whose properties match the given {@code queryParams}
+        * @param queryParams The key/value pairs which are found in {@link ChampObject#getProperties}
+        * @return A {@link Stream} where each {@link ChampObject#getProperties} contains the {@code queryParams}
+        */
+       public Stream<ChampObject> queryObjects(Map<String, Object> queryParams);
+
+        /**
+        * Create/Update a relationship.  If the ChampRelationship key is present, an update will be attempted,
+        * otherwise a create will be attempted.  Each implementation has different guarantees on
+        * validation - see the specific implementation for more details on this.
+        * @param relationship - The ChampRelationship that you wish to store in the graph
+        * @return The ChampRelationship as it was stored
+        * @throws ChampMarshallingException If the {@code relationship} is not able to be marshalled into the backend representation
+        * @throws ChampSchemaViolationException If the {@code relationship} violates the constraints specifed by {@link ChampGraph#retrieveSchema}
+        * @throws ChampObjectNotExistsException If either the source or target object referenced by this relationship does not exist in the graph
+        * @throws ChampRelationshipNotExistsException If {@link org.openecomp.aai.champ.model.ChampRelationship#getKey}.isPresent() but the object cannot be found in the graph 
+        * @throws ChampUnmarshallingException If the edge which was created could not be unmarshalled into a ChampRelationship
+        */
+       public ChampRelationship storeRelationship(ChampRelationship relationship) throws ChampMarshallingException, ChampObjectNotExistsException, ChampSchemaViolationException, ChampRelationshipNotExistsException, ChampUnmarshallingException;
+       
+        /**
+        * Replace a relationship. ChampRelationship key is mandatory .The main purpose of this method is to replace the entire properties of an existing relationship .Source/Target can't be updated with this method
+        * Each implementation has different guarantees on
+        * validation - see the specific implementation for more details on this.
+        * @param relationship - The ChampRelationship that you wish to replace in the graph
+        * @return The ChampRelationship as it was stored
+        * @throws ChampMarshallingException If the {@code relationship} is not able to be marshalled into the backend representation
+        * @throws ChampSchemaViolationException If the {@code relationship} violates the constraints specifed by {@link ChampGraph#retrieveSchema}
+        * @throws ChampRelationshipNotExistsException If {@link org.openecomp.aai.champ.model.ChampRelationship#getKey} is not present or object not found in the graph 
+        * @throws ChampUnmarshallingException If the edge which was created could not be unmarshalled into a ChampRelationship
+        */
+       public ChampRelationship replaceRelationship(ChampRelationship relationship) throws ChampMarshallingException, ChampSchemaViolationException, ChampRelationshipNotExistsException, ChampUnmarshallingException; 
+
+       
+       /**
+        * Retrieve a relationship by its key.
+        * @param key The key of the ChampRelationship in the graph {@link org.openecomp.aai.champ.model.ChampRelationship#getKey()}
+        * @return The {@link org.openecomp.aai.champ.model.ChampRelationship} if it was present, otherwise {@link Optional#empty()}
+        * @throws ChampUnmarshallingException If the relationship was found, but could not be unmarshalled
+        */
+       public Optional<ChampRelationship> retrieveRelationship(Object key) throws ChampUnmarshallingException;
+
+        /**
+        * Delete a relationship by its key.
+        * @param relationship The ChampRelationship in the graph ({@link ChampRelationship#getKey must be present})
+        * @throws ChampRelationshipNotExistsException If the object did not exist in the graph
+        */
+       public void deleteRelationship(ChampRelationship relationship) throws ChampRelationshipNotExistsException;
+
+       /**
+        * Retrieve the relationships which are incident to the {@code object}
+        * @param object The object you wish to find incident relationships for
+        * @return A {@link Stream} where each {@link ChampRelationship} has this {@code object} as either a source or target object
+        * @throws ChampUnmarshallingException If any of the ChampRelationship objects could not be unmarshalled
+        * @throws ChampObjectNotExistsException If the {@code object} does not exist in this graph
+        */
+       public Stream<ChampRelationship> retrieveRelationships(ChampObject object) throws ChampUnmarshallingException, ChampObjectNotExistsException;
+
+       /**
+        * Retrieve the relationships whose properties match the given {@code queryParams}
+        * @param queryParams The key/value pairs to search for in the {@link ChampRelationship#getProperties}
+        * @return A {@link Stream} where each {@link ChampRelationship#getProperties} contains the {@code queryParams}
+        */
+       public Stream<ChampRelationship> queryRelationships(Map<String, Object> queryParams);
+
+       /**
+        * Create/Update a {@link ChampPartition}.  If any of the ChampObjects or ChampRelationships
+        * present in this ChampPartition already exist, an update will be attempted, otherwise a create
+        * will be attempted.  Each implementation has different guarantees on validation -
+        * see the specific implementation details for more information on this.
+        * @param partition The ChampPartition you wish to store in this graph
+        * @throws ChampMarshallingException If any of the objects or relationships contained in this
+        *                                                                              partition could not be marshalled into its backed representation
+        * @throws ChampObjectNotExistsException If any of the objects being updated do not exist, or if a relationship
+        *                                                                                      contain objects which do not exist in the graph.
+        * @throws ChampSchemaViolationException If any of the objects or relationships violate the schema provided by {@link retrieveSchema}
+        * @throws ChampRelationshipNotExistsException If any of the relationships which are being updated do not exist
+        * @return The ChampPartition as is was stored in the graph (contains keys for each newly created object)
+        */
+       public ChampPartition storePartition(ChampPartition partition) throws ChampMarshallingException, ChampObjectNotExistsException, ChampSchemaViolationException, ChampRelationshipNotExistsException;
+
+       /**
+        * Delete the {@code partition} from the graph
+        * @param partition The partition to delete from the graph
+        */
+       public void deletePartition(ChampPartition partition);
+
+       /**
+        * Create/Update an object index on the graph
+        * @param index The index to create on this {@code graph}
+        */
+       public void storeObjectIndex(ChampObjectIndex index);
+
+       /**
+        * Retrieve an object index on the graph by its {@code indexName}
+        * @param indexName The name of the index to retrieve from the graph
+        * @return The {@link ChampObjectIndex} which matches the given @{code indexName} in the graph
+        */
+       public Optional<ChampObjectIndex> retrieveObjectIndex(String indexName);
+
+       /**
+        * Retrieve the object indices on the graph
+        * @return A {@link Stream} where each {@link ChampObjectIndex} exists in the graph
+        */
+       public Stream<ChampObjectIndex> retrieveObjectIndices();
+
+       /**
+        * Delete the object index on the graph by its {@code indexName}
+        * @param indexName The name of the index to delete from the graph
+        * @throws ChampIndexNotExistsException If an index does not exist with the given {@code indexName} in the graph
+        */
+       public void deleteObjectIndex(String indexName) throws ChampIndexNotExistsException;
+
+       /**
+        * Create/Update a relationship index on the graph
+        * @param index The relationship index to create on the graph
+        */
+       public void storeRelationshipIndex(ChampRelationshipIndex index);
+
+       /**
+        * Retrieve a relationship index from the graph
+        * @param indexName The name of the relationship index to retrieve from the graph
+        * @return The {@link ChampRelationshipIndex} which matches the given {@code indexName} in the graph
+        *                      or {@link Optional#empty} if no such relationship index exists
+        */
+       public Optional<ChampRelationshipIndex> retrieveRelationshipIndex(String indexName);
+
+       /**
+        * Retrieve the relationship indices from the graph
+        * @return A {@link Stream} where each {@link ChampRelationshipIndex} exists in the graph
+        */
+       public Stream<ChampRelationshipIndex> retrieveRelationshipIndices();
+
+       /**
+        * Delete a relationship index from the graph
+        * @param indexName THe name of the index to delete from the graph
+        * @throws ChampIndexNotExistsException If an index does not exist with the give {@code indexName} in the graph
+        */
+       public void deleteRelationshipIndex(String indexName) throws ChampIndexNotExistsException;
+
+       /**
+        * Create/Update the schema for a graph
+        * @param schema The {@link ChampSchema} to create or update on the graph
+        * @throws ChampSchemaViolationException If this schema update would violate the current schema
+        */
+       public void storeSchema(ChampSchema schema) throws ChampSchemaViolationException;
+
+       /**
+        * Retrieve the schema for a graph
+        * @return The {@link ChampSchema} for the graph
+        */
+       public ChampSchema retrieveSchema();
+
+       /**
+        * Create/Update an object constraint on a schema
+        * @param objectConstraint The object constraint you wish to create/update for the graph
+        * @throws ChampSchemaViolationException If this schema update would violate the current schema
+        */
+       public void updateSchema(ChampObjectConstraint objectConstraint) throws ChampSchemaViolationException;
+
+       /**
+        * Create/Update a relationship constraint on a schema
+        * @param schema The relationship constraint you wish to create/update for the graph
+        * @throws ChampSchemaViolationException If this schema update would violate the current schema
+        */
+       public void updateSchema(ChampRelationshipConstraint schema) throws ChampSchemaViolationException;
+
+       /**
+        * Delete the schema for a graph
+        */
+       public void deleteSchema();
+
+       /**
+        * Shutdown the ChampAPI. It is up to the caller to synchronize access to the ChampAPI
+        * so that shutting it down does not interfere with concurrent operations.
+        */
+       public void shutdown();
+
+       /**
+        * Used to determine what the outcome of certain ChampGraph operations will be.  For example,
+        * if this graph is not capable of deleting object indices, you can expect those calls to fail.
+        * @see ChampCapabilities
+        * @return What this graph is capable of performing
+        */
+        public ChampCapabilities capabilities();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/event/AbstractLoggingChampGraph.java b/src/main/java/org/openecomp/aai/champ/event/AbstractLoggingChampGraph.java
new file mode 100644 (file)
index 0000000..a8e0e2f
--- /dev/null
@@ -0,0 +1,496 @@
+/**
+ * ============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.openecomp.aai.champ.event;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.security.GeneralSecurityException;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+
+import org.openecomp.aai.champ.ChampCapabilities;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.event.ChampEvent.ChampOperation;
+import org.openecomp.aai.champ.exceptions.ChampIndexNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.att.nsa.cambria.client.CambriaClientBuilders;
+import com.att.nsa.cambria.client.CambriaPublisher;
+
+
+/**
+ * This class provides the hooks to allow Champ operations to be logged to an event
+ * stream.
+ */
+public abstract class AbstractLoggingChampGraph implements ChampGraph {
+
+  private static final Logger logger = LoggerFactory.getLogger(AbstractLoggingChampGraph.class);
+  
+  public abstract ChampObject                      executeStoreObject(ChampObject object) throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException;
+  public abstract ChampObject                      executeReplaceObject(ChampObject object) throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException;
+  public abstract Optional<ChampObject>            retrieveObject(Object key) throws ChampUnmarshallingException;
+  public abstract void                             executeDeleteObject(Object key) throws ChampObjectNotExistsException;
+  public abstract Stream<ChampObject>              queryObjects(Map<String, Object> queryParams);
+  public abstract ChampRelationship                executeStoreRelationship(ChampRelationship relationship) throws ChampUnmarshallingException, ChampMarshallingException, ChampObjectNotExistsException, ChampSchemaViolationException, ChampRelationshipNotExistsException;  
+  public abstract ChampRelationship                executeReplaceRelationship(ChampRelationship relationship) throws ChampUnmarshallingException, ChampMarshallingException, ChampSchemaViolationException, ChampRelationshipNotExistsException; 
+  public abstract Optional<ChampRelationship>      retrieveRelationship(Object key) throws ChampUnmarshallingException;
+  public abstract void                             executeDeleteRelationship(ChampRelationship relationship) throws ChampRelationshipNotExistsException;
+  public abstract Stream<ChampRelationship>        retrieveRelationships(ChampObject object) throws ChampUnmarshallingException, ChampObjectNotExistsException;
+  public abstract Stream<ChampRelationship>        queryRelationships(Map<String, Object> queryParams);
+  public abstract ChampPartition                   executeStorePartition(ChampPartition partition) throws ChampSchemaViolationException, ChampRelationshipNotExistsException, ChampMarshallingException, ChampObjectNotExistsException;
+  public abstract void                             executeDeletePartition(ChampPartition graph);
+  public abstract void                             executeStoreObjectIndex(ChampObjectIndex index);
+  public abstract Optional<ChampObjectIndex>       retrieveObjectIndex(String indexName);
+  public abstract Stream<ChampObjectIndex>         retrieveObjectIndices();
+  public abstract void                             executeDeleteObjectIndex(String indexName) throws ChampIndexNotExistsException;
+  public abstract void                             executeStoreRelationshipIndex(ChampRelationshipIndex index);
+  public abstract Optional<ChampRelationshipIndex> retrieveRelationshipIndex(String indexName);
+  public abstract Stream<ChampRelationshipIndex>   retrieveRelationshipIndices();
+  public abstract void                             executeDeleteRelationshipIndex(String indexName) throws ChampIndexNotExistsException;
+  public abstract void                             storeSchema(ChampSchema schema) throws ChampSchemaViolationException;
+  public abstract ChampSchema                      retrieveSchema();
+  public abstract void                             updateSchema(ChampObjectConstraint objectConstraint) throws ChampSchemaViolationException;
+  public abstract void                             updateSchema(ChampRelationshipConstraint schema) throws ChampSchemaViolationException;
+  public abstract void                             deleteSchema();
+  public abstract ChampCapabilities                capabilities();
+
+   
+  /** Configuration property for setting the comma-separated list of servers to use for
+   *  communicating with the event bus. */
+  public final static String  PARAM_EVENT_STREAM_HOSTS      = "champ.event.stream.hosts";
+  
+  /** Configuration property for setting the number of events that we will try to 'batch' 
+   *  up before sending them to the event bus. */
+  public final static String  PARAM_EVENT_STREAM_BATCH_SIZE = "champ.event.stream.batch-size";
+  public final static Integer DEFAULT_EVENT_STREAM_BATCH_SIZE = 1;
+  
+  /** Configuration property for setting the maximum amount of time to wait for a batch of
+   *  outgoing messages to fill up before sending the batch. */
+  public final static String  PARAM_EVENT_STREAM_BATCH_TIMEOUT = "champ.event.stream.batch-timeout";
+  public final static Integer DEFAULT_EVENT_STREAM_BATCH_TIMEOUT_MS = 500; 
+  
+  public final static String  PARAM_EVENT_STREAM_PUBLISHER_POOL_SIZE = "champ.event.stream.publisher-pool-size";
+  public final static Integer DEFAULT_EVENT_STREAM_PUBLISHER_POOL_SIZE = 100;
+  
+  /** The event stream topic that we will publish Champ events to. */
+  public final static String EVENT_TOPIC = "champRawEvents";
+    
+  /** Number of events to 'batch up' before actually publishing them to the event bus. */
+  private Integer eventStreamBatchSize;
+  
+  private Integer eventStreamBatchTimeout;
+  
+  private Integer eventStreamPublisherPoolSize;
+  
+  /** Comma-separated list of hosts for connecting to the event bus. */
+  private String  eventStreamHosts = null;
+  
+  /** Client used for publishing messages to the event bus. */
+  protected CambriaPublisher producer;
+
+  /** Pool of worker threads that do the work of publishing the events to the event bus. */
+  protected ThreadPoolExecutor publisherPool;
+  
+  
+  /**
+   * Create a new instance of the AbstractLoggingChampGraph.
+   * 
+   * @param properties - Set of configuration properties for this graph instance.
+   */
+  protected AbstractLoggingChampGraph(Map<String, Object> properties) {
+    
+    // Extract the necessary parameters from the configuration properties.
+    configure(properties);
+      
+    // Create the executor pool that will do the work of publishing events to the event bus.
+    publisherPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(eventStreamPublisherPoolSize);
+    
+    // Make sure that we are actually provided a list of hosts for connecting to the event
+    // bus before we actually try to do anything useful.
+    if(eventStreamHosts == null) {
+      
+      // We were not supplied a list of event bus hosts, so just bail.
+      logger.error("Cannot initialize event stream publisher without at least one event bus host.");
+      logger.error("NOTE!! Champ events will NOT be published to the event stream!");
+      return;
+    }
+         
+    try {
+        
+      // Instantiate the producer that we will use to publish events to the event stream.
+      setProducer(new CambriaClientBuilders.PublisherBuilder()
+                        .usingHosts(eventStreamHosts)
+                        .onTopic(EVENT_TOPIC)
+                        .limitBatch(eventStreamBatchSize, eventStreamBatchTimeout)
+                        .build());
+      
+    } catch (MalformedURLException | GeneralSecurityException e) {
+      
+      logger.error("Could not instantiate event stream producer due to: " + e.getMessage());
+      logger.error("NOTE: Champ events will NOT be published to the event stream");
+      producer = null;
+    }
+  }
+
+      
+  /**
+   * Process the configuration properties supplied for this graph instance.
+   * 
+   * @param properties - Configuration parameters.
+   */
+  private void configure(Map<String, Object> properties) {
+    
+    eventStreamBatchSize = 
+        (Integer) getProperty(properties, PARAM_EVENT_STREAM_BATCH_SIZE,    DEFAULT_EVENT_STREAM_BATCH_SIZE);
+    eventStreamBatchTimeout = 
+        (Integer) getProperty(properties, PARAM_EVENT_STREAM_BATCH_TIMEOUT, DEFAULT_EVENT_STREAM_BATCH_TIMEOUT_MS);
+    eventStreamPublisherPoolSize = 
+        (Integer) getProperty(properties, PARAM_EVENT_STREAM_PUBLISHER_POOL_SIZE, DEFAULT_EVENT_STREAM_PUBLISHER_POOL_SIZE);
+    
+    if(properties.containsKey(PARAM_EVENT_STREAM_HOSTS)) {
+      eventStreamHosts = (String) properties.get(PARAM_EVENT_STREAM_HOSTS);
+    } 
+  }
+  
+  public void setProducer(CambriaPublisher aProducer) {
+    
+    producer = aProducer;
+  }
+  
+  private Object getProperty(Map<String, Object> properties, String property, Object defaultValue) {
+    
+    if(properties.containsKey(property)) {
+      return properties.get(property);
+    } else {
+      return defaultValue;
+    }
+  }
+  
+  @Override
+  public void shutdown() {
+    
+    if(publisherPool != null) {
+      publisherPool.shutdown();
+      
+      try {
+        publisherPool.awaitTermination(1000, TimeUnit.MILLISECONDS);
+      } catch (InterruptedException e) {}
+    }
+    
+    if(producer != null) {
+      producer.close();
+    }
+  }
+  
+  public ChampObject storeObject(ChampObject object) throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException {
+    
+    ChampObject storedObject = executeStoreObject(object);
+    
+    if(storedObject != null) {
+      
+      // Update the event stream with the current operation.
+      logEvent(ChampEvent.builder()
+                    .operation(ChampOperation.STORE)
+                    .entity(storedObject)
+                    .build());
+    }
+    
+    return storedObject;
+  }
+  
+  
+  public ChampObject replaceObject(ChampObject object)
+      throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException {
+    
+    ChampObject replacedObject = executeReplaceObject(object);
+    
+    if(replacedObject != null) {
+      
+      // Update the event stream with the current operation.
+      logEvent(ChampEvent.builder()
+                    .operation(ChampOperation.REPLACE)
+                    .entity(replacedObject)
+                    .build());
+    }
+    
+    return replacedObject;
+  }
+  
+
+  public void deleteObject(Object key) throws ChampObjectNotExistsException {
+
+    // Retrieve the object that we are deleting before it's gone, so that we can 
+    // report it to the event stream.
+    Optional<ChampObject> objectToDelete = Optional.empty();
+    try {
+      objectToDelete = retrieveObject(key);
+      
+    } catch (ChampUnmarshallingException e) {
+      logger.error("Unable to generate delete object log: " + e.getMessage());
+    }
+    
+    executeDeleteObject(key);
+    
+    if(objectToDelete.isPresent()) {
+      // Update the event stream with the current operation.
+      logEvent(ChampEvent.builder()
+                    .operation(ChampOperation.DELETE)
+                    .entity(objectToDelete.get())
+                    .build());
+    }
+  }
+  
+  
+  public ChampRelationship storeRelationship(ChampRelationship relationship)
+      throws ChampUnmarshallingException, ChampMarshallingException, ChampObjectNotExistsException, ChampSchemaViolationException, ChampRelationshipNotExistsException {  
+
+    ChampRelationship storedRelationship = executeStoreRelationship(relationship);
+    
+    if(storedRelationship != null) {
+      
+      // Update the event stream with the current operation.
+      logEvent(ChampEvent.builder()
+                    .operation(ChampOperation.STORE)
+                    .entity(storedRelationship)
+                    .build());
+    }
+    
+    return storedRelationship;
+  }
+  
+  
+  public ChampRelationship replaceRelationship(ChampRelationship relationship)
+      throws ChampUnmarshallingException, ChampMarshallingException, ChampSchemaViolationException, ChampRelationshipNotExistsException { 
+
+    ChampRelationship replacedRelationship = executeReplaceRelationship(relationship);
+    
+    if(replacedRelationship != null) {
+      
+      // Update the event stream with the current operation.
+      logEvent(ChampEvent.builder()
+                    .operation(ChampOperation.REPLACE)
+                    .entity(replacedRelationship)
+                    .build());
+    }
+    
+    return replacedRelationship;
+  }
+  
+  
+  public void deleteRelationship(ChampRelationship relationship) throws ChampRelationshipNotExistsException {
+
+    executeDeleteRelationship(relationship);
+    
+    // Update the event stream with the current operation.
+    logEvent(ChampEvent.builder()
+                  .operation(ChampOperation.DELETE)
+                  .entity(relationship)
+                  .build());
+  }
+  
+  
+  public ChampPartition storePartition(ChampPartition partition) throws ChampSchemaViolationException, ChampRelationshipNotExistsException, ChampMarshallingException, ChampObjectNotExistsException {
+
+    ChampPartition storedPartition = executeStorePartition(partition);
+    
+    if(storedPartition != null) {
+      
+      // Update the event stream with the current operation.
+      logEvent(ChampEvent.builder()
+                    .operation(ChampOperation.STORE)
+                    .entity(storedPartition)
+                    .build());
+    }
+    
+    return storedPartition;
+  }
+  
+  
+  public void deletePartition(ChampPartition graph) {
+
+    executeDeletePartition(graph);
+    
+    // Update the event stream with the current operation.
+    logEvent(ChampEvent.builder()
+                  .operation(ChampOperation.DELETE)
+                  .entity(graph)
+                  .build());
+  }
+  
+  
+  public void storeObjectIndex(ChampObjectIndex index) {
+
+    executeStoreObjectIndex(index);
+    
+    // Update the event stream with the current operation.
+    logEvent(ChampEvent.builder()
+                  .operation(ChampOperation.STORE)
+                  .entity(index)
+                  .build());
+  }
+  
+  
+  public void deleteObjectIndex(String indexName) throws ChampIndexNotExistsException {
+    
+    // Retrieve the index that we are deleting before it's gone, so that we can 
+    // report it to the event stream.
+    Optional<ChampObjectIndex> indexToDelete = retrieveObjectIndex(indexName);
+    
+    executeDeleteObjectIndex(indexName);
+    
+    if(indexToDelete.isPresent()) {
+      // Update the event stream with the current operation.
+      logEvent(ChampEvent.builder()
+                    .operation(ChampOperation.DELETE)
+                    .entity(indexToDelete.get()) 
+                    .build());
+    }
+  }
+  
+  
+  public void storeRelationshipIndex(ChampRelationshipIndex index) {
+
+    executeStoreRelationshipIndex(index);
+    
+    // Update the event stream with the current operation.
+    logEvent(ChampEvent.builder()
+                  .operation(ChampOperation.STORE)
+                  .entity(index) 
+                  .build());
+  }
+  
+  
+  public void deleteRelationshipIndex(String indexName) throws ChampIndexNotExistsException {
+
+    // Retrieve the index that we are deleting before it's gone, so that we can 
+    // report it to the event stream.
+    Optional<ChampRelationshipIndex> indexToDelete = retrieveRelationshipIndex(indexName);
+    
+    executeDeleteRelationshipIndex(indexName);
+    
+    if(indexToDelete.isPresent()) {
+      // Update the event stream with the current operation.
+      logEvent(ChampEvent.builder()
+                    .operation(ChampOperation.DELETE)
+                    .entity(indexToDelete.get()) 
+                    .build());
+    }
+  }
+  
+  
+  /**
+   * Submits an event to be published to the event stream.
+   * 
+   * @param anEvent - The event to be published.
+   */
+  public void logEvent(ChampEvent anEvent) {
+    
+    if(logger.isDebugEnabled()) {
+      logger.debug("Submitting event to be published to the event bus: " + anEvent.toString());
+    }
+    
+    try {
+      
+      // Try submitting the event to be published to the event bus.
+      publisherPool.execute(new EventPublisher(anEvent));
+    
+    } catch (RejectedExecutionException re) {
+      logger.error("Event could not be published to the event bus due to: " + re.getMessage());
+      
+    } catch (NullPointerException npe) {
+      logger.error("Can not publish null event to event bus.");
+    }
+  }
+  
+  
+  /**
+   * This class runs as a background thread and is responsible for pulling Champ events off
+   * of the internal queue and publishing them to the event stream.
+   */
+  private class EventPublisher implements Runnable {
+    
+    /** Partition key to use when publishing events to the event stream.  We WANT all events
+     *  to go to a single partition, so we are just using a hard-coded key for every event. */
+    private static final String EVENTS_PARTITION_KEY = "champEventKey";
+    
+    private ChampEvent event;
+    
+    public EventPublisher(ChampEvent event) {
+      this.event = event;
+    }
+    
+    
+    @Override
+    public void run() {
+
+      boolean done = false;
+      while(!done && !Thread.currentThread().isInterrupted()) {
+        try {
+          
+          // Make sure that we actually have a producer instance to use to publish
+          // the event with.
+          if(producer != null) {
+            
+            // Try publishing the event to the event bus.
+            producer.send(EVENTS_PARTITION_KEY, event.toJson());
+            
+          } else if (logger.isDebugEnabled()) {            
+            logger.debug("Event bus producer is not instantiated - will not attempt to publish event");
+          }
+          
+          done = true;
+          
+        } catch (IOException e) {
+  
+          // We were unable to publish to the event bus, so wait a bit and then try
+          // again.
+          try {
+            Thread.sleep(500);
+            
+          } catch (InterruptedException e1) {
+            logger.info("Stopping event publisher worker thread.");
+            return;
+          }
+        }           
+      }
+    }
+  }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/event/ChampEvent.java b/src/main/java/org/openecomp/aai/champ/event/ChampEvent.java
new file mode 100644 (file)
index 0000000..f56601d
--- /dev/null
@@ -0,0 +1,186 @@
+/**
+ * ============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.openecomp.aai.champ.event;
+
+
+import java.io.IOException;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+
+public class ChampEvent {
+
+  public enum ChampOperation {
+    STORE,
+    REPLACE,
+    DELETE
+  }
+  
+  private static ObjectMapper mapper = new ObjectMapper();
+  
+  private ChampOperation         operation;
+  private long                   timestamp;
+  private ChampObject            vertex            = null;
+  private ChampRelationship      relationship      = null;
+  private ChampPartition         partition         = null;
+  private ChampObjectIndex       objectIndex       = null;
+  private ChampRelationshipIndex relationshipIndex = null;
+  
+  
+  public static Builder builder() {
+    return new Builder();
+  }
+  
+  public ChampOperation getOperation() {
+    return operation;
+  }
+  
+  public void setOperation(ChampOperation operation) {
+    this.operation = operation;
+  }
+    
+  public long getTimestamp() {
+    return timestamp;
+  }
+  
+  public void setTimestamp(long timestamp) {
+    this.timestamp = timestamp;
+  }
+  
+  public ChampObject getVertex() {
+    return vertex;
+  }
+  
+  public void setVertex(ChampObject vertex) {
+    this.vertex = vertex;
+  }
+  
+  public ChampRelationship getRelationship() {
+    return relationship;
+  }
+  
+  public void setRelationship(ChampRelationship relationship) {
+    this.relationship = relationship;
+  }
+  
+  public ChampPartition getPartition() {
+    return partition;
+  }
+  
+  public void setPartition(ChampPartition partition) {
+    this.partition = partition;
+  }
+  
+  public ChampObjectIndex getObjectIndex() {
+    return objectIndex;
+  }
+  
+  public void setObjectIndex(ChampObjectIndex index) {
+    this.objectIndex = index;
+  }
+  
+  public ChampRelationshipIndex getRelationshipIndex() {
+    return relationshipIndex;
+  }
+  
+  public void setRelationshipIndex(ChampRelationshipIndex relationshipIndex) {
+    this.relationshipIndex = relationshipIndex;
+  }
+  
+  public String toJson() {
+    
+    ObjectMapper mapper = new ObjectMapper();
+    mapper.setSerializationInclusion(Include.NON_NULL);
+    
+    try {
+      return mapper.writeValueAsString(this);
+    } catch (JsonProcessingException e) {
+      return "Unmarshallable: " + e.getMessage();
+    }
+  }
+  
+  public static ChampEvent fromJson(String json) throws JsonParseException, JsonMappingException, IOException {
+
+    mapper.setSerializationInclusion(Include.NON_NULL);
+    return mapper.readValue(json, ChampEvent.class);
+  }
+  @Override
+  public String toString() {
+    return toJson();
+  }
+  
+  public static class Builder {
+    
+    ChampEvent event = null;
+    
+    
+    public Builder() {
+      event = new ChampEvent();
+    }
+    
+    public Builder operation(ChampOperation operation) {
+      event.setOperation(operation);
+      return this;
+    }
+    
+    public Builder entity(ChampObject entity) {
+      event.setVertex(entity);
+      return this;
+    }
+    
+    public Builder entity(ChampRelationship relationship) {
+      event.relationship = relationship;
+      return this;
+    }
+    
+    public Builder entity(ChampPartition partition) {
+      event.partition = partition;
+      return this;
+    }
+    
+    public Builder entity(ChampObjectIndex index) {
+      event.objectIndex = index;
+      return this;
+    }
+    
+    public Builder entity(ChampRelationshipIndex relationshipIndex) {
+      event.relationshipIndex = relationshipIndex;
+      return this;
+    }
+   
+    public ChampEvent build() {
+      
+      event.setTimestamp(System.currentTimeMillis());
+      return event;
+    }
+  }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/exceptions/ChampIndexNotExistsException.java b/src/main/java/org/openecomp/aai/champ/exceptions/ChampIndexNotExistsException.java
new file mode 100644 (file)
index 0000000..806d91a
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * ============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.openecomp.aai.champ.exceptions;
+
+public final class ChampIndexNotExistsException extends Exception {
+
+       private static final long serialVersionUID = 1690478892404278379L;
+
+       public ChampIndexNotExistsException() {}
+
+       public ChampIndexNotExistsException(String message) {
+               super(message);
+       }
+
+       public ChampIndexNotExistsException(Throwable cause) {
+               super(cause);
+       }
+
+       public ChampIndexNotExistsException(String message, Throwable cause) {
+               super(message, cause);
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/exceptions/ChampMarshallingException.java b/src/main/java/org/openecomp/aai/champ/exceptions/ChampMarshallingException.java
new file mode 100644 (file)
index 0000000..33797ab
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * ============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.openecomp.aai.champ.exceptions;
+
+public final class ChampMarshallingException extends Exception {
+
+       private static final long serialVersionUID = 7962962670920382670L;
+
+       public ChampMarshallingException() {}
+       
+       public ChampMarshallingException(String message) {
+               super(message);
+       }
+       
+       public ChampMarshallingException(Throwable cause) {
+               super(cause);
+       }
+       
+       public ChampMarshallingException(String message, Throwable cause) {
+               super(message, cause);
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/exceptions/ChampObjectNotExistsException.java b/src/main/java/org/openecomp/aai/champ/exceptions/ChampObjectNotExistsException.java
new file mode 100644 (file)
index 0000000..1651a45
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * ============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.openecomp.aai.champ.exceptions;
+
+public final class ChampObjectNotExistsException extends Exception {
+       
+       private static final long serialVersionUID = -4365365939154593814L;
+
+       public ChampObjectNotExistsException() {}
+       
+       public ChampObjectNotExistsException(String message) {
+               super(message);
+       }
+       
+       public ChampObjectNotExistsException(Throwable cause) {
+               super(cause);
+       }
+       
+       public ChampObjectNotExistsException(String message, Throwable cause) {
+               super(message, cause);
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/exceptions/ChampRelationshipNotExistsException.java b/src/main/java/org/openecomp/aai/champ/exceptions/ChampRelationshipNotExistsException.java
new file mode 100644 (file)
index 0000000..f4c40d7
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * ============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.openecomp.aai.champ.exceptions;
+
+public final class ChampRelationshipNotExistsException extends Exception {
+
+       private static final long serialVersionUID = -3006050460369110202L;
+
+       public ChampRelationshipNotExistsException() {}
+
+       public ChampRelationshipNotExistsException(String message) {
+               super(message);
+       }
+       
+       public ChampRelationshipNotExistsException(Throwable cause) {
+               super(cause);
+       }
+       
+       public ChampRelationshipNotExistsException(String message, Throwable cause) {
+               super(message, cause);
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/exceptions/ChampSchemaViolationException.java b/src/main/java/org/openecomp/aai/champ/exceptions/ChampSchemaViolationException.java
new file mode 100644 (file)
index 0000000..7521b2f
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * ============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.openecomp.aai.champ.exceptions;
+
+public final class ChampSchemaViolationException extends Exception {
+
+       private static final long serialVersionUID = -6650058224577965021L;
+
+       public ChampSchemaViolationException() {}
+       
+       public ChampSchemaViolationException(String message) {
+               super(message);
+       }
+       
+       public ChampSchemaViolationException(Throwable cause) {
+               super(cause);
+       }
+       
+       public ChampSchemaViolationException(String message, Throwable cause) {
+               super(message, cause);
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/exceptions/ChampUnmarshallingException.java b/src/main/java/org/openecomp/aai/champ/exceptions/ChampUnmarshallingException.java
new file mode 100644 (file)
index 0000000..619c991
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * ============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.openecomp.aai.champ.exceptions;
+
+public final class ChampUnmarshallingException extends Exception {
+
+       private static final long serialVersionUID = 8162385108397238865L;
+
+       public ChampUnmarshallingException() {}
+
+       public ChampUnmarshallingException(String message) {
+               super(message);
+       }
+       
+       public ChampUnmarshallingException(Throwable cause) {
+               super(cause);
+       }
+       
+       public ChampUnmarshallingException(String message, Throwable cause) {
+               super(message, cause);
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/graph/impl/AbstractGremlinChampGraph.java b/src/main/java/org/openecomp/aai/champ/graph/impl/AbstractGremlinChampGraph.java
new file mode 100644 (file)
index 0000000..130693f
--- /dev/null
@@ -0,0 +1,350 @@
+/**
+ * ============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.openecomp.aai.champ.graph.impl;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.graph.impl.TitanChampGraphImpl.Builder;
+import org.openecomp.aai.champ.model.ChampElement;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.partition.CreateChampPartitionable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractGremlinChampGraph extends AbstractValidatingChampGraph {
+
+       private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGremlinChampGraph.class);
+
+       protected abstract GraphTraversalSource startTraversal();
+       protected abstract Stream<ChampElement> runTraversal(GraphTraversal<?, ?> traversal);
+
+       protected AbstractGremlinChampGraph(Map<String, Object> properties) {
+         super(properties);
+       }
+       
+       @Override
+       public Optional<ChampObject> retrieveObject(Object key) throws ChampUnmarshallingException {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               final Stream<ChampElement> elements = runTraversal(startTraversal().V(key).limit(1));
+
+               if (elements.count() == 0) {
+                       return Optional.empty();
+               }
+
+               return Optional.of(elements.findFirst().get().asObject());
+       }
+
+       public void executeDeleteObject(Object key) throws ChampObjectNotExistsException {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               final Stream<ChampElement> elements = runTraversal(startTraversal().V(key).limit(1));
+
+               if (elements.count() == 0) {
+                       throw new ChampObjectNotExistsException();
+               }
+
+               runTraversal(startTraversal().V(key).drop());
+       }
+
+       @Override
+       public Stream<ChampObject> queryObjects(Map<String, Object> queryParams) {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               //If they provided the object key, do this the quick way rather than creating a traversal
+               if (queryParams.containsKey(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_KEY.toString())) {
+                       try {
+                               final Optional<ChampObject> object = retrieveObject(queryParams.get(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_KEY.toString()));
+                       
+                               if (object.isPresent()) return Stream.of(object.get());
+                               else return Stream.empty();
+                       } catch (ChampUnmarshallingException e) {
+                               LOGGER.warn("Failed to unmarshall object", e);
+                               return Stream.empty();
+                       }
+               }
+
+               final GraphTraversal<?, Vertex> traversal = startTraversal().V();
+
+               for (Entry<String, Object> filter : queryParams.entrySet()) {
+                       if (filter.getKey().equals(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString())) {
+                               continue; //For performance reasons, the label is the last thing to be added
+                       } else {
+                               traversal.has(filter.getKey(), filter.getValue());
+                       }
+               }
+
+               if (queryParams.containsKey(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString())) {
+                       traversal.hasLabel((String) queryParams.get(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString()));
+               }
+
+
+               return runTraversal(traversal).map(element -> {
+                       return element.asObject(); //Safe, since we're only operating on vertices
+               });
+       }
+
+       @Override
+       public Optional<ChampRelationship> retrieveRelationship(Object key) throws ChampUnmarshallingException {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               final Stream<ChampElement> elements = runTraversal(startTraversal().E(key).limit(1));
+
+               if (elements.count() == 0) return Optional.empty();
+
+               return Optional.of(elements.findFirst().get().asRelationship());
+       }
+
+       public void executeDeleteRelationship(ChampRelationship relationship) throws ChampRelationshipNotExistsException {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+               if (!relationship.getKey().isPresent()) throw new IllegalArgumentException("Key must be provided when deleting a relationship");
+
+               final Stream<ChampElement> elements = runTraversal(startTraversal().E(relationship.getKey().get()).limit(1));
+
+               if (elements.count() == 0) {
+                       throw new ChampRelationshipNotExistsException();
+               }
+
+               runTraversal(startTraversal().E(relationship.getKey().get()).drop());
+       }
+
+       @Override
+       public Stream<ChampRelationship> retrieveRelationships(ChampObject object)
+                       throws ChampUnmarshallingException, ChampObjectNotExistsException {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               final Stream<ChampElement> elements = runTraversal(startTraversal().V(object.getKey().get()).limit(1).bothE());
+
+               return elements.map(element -> {
+                       return element.asRelationship(); //Safe, since we're only operating on edges
+               });
+       }
+
+       @Override
+       public Stream<ChampRelationship> queryRelationships(Map<String, Object> queryParams) {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               //If they provided the relationship key, do this the quick way rather than creating a traversal
+               if (queryParams.containsKey(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_KEY.toString())) {
+                       try {
+                               final Optional<ChampRelationship> relationship = retrieveRelationship(queryParams.get(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_KEY.toString()));
+                       
+                               if (relationship.isPresent()) return Stream.of(relationship.get());
+                               else return Stream.empty();
+                       } catch (ChampUnmarshallingException e) {
+                               LOGGER.warn("Failed to unmarshall relationship", e);
+                               return Stream.empty();
+                       }
+               }
+
+               final GraphTraversal<Edge, Edge> traversal = startTraversal().E();
+
+               for (Entry<String, Object> filter : queryParams.entrySet()) {
+                       if (filter.getKey().equals(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString())) {
+                               continue; //Add the label last for performance reasons
+                       } else {
+                               traversal.has(filter.getKey(), filter.getValue());
+                       }
+               }
+
+               if (queryParams.containsKey(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString())) {
+                       traversal.hasLabel((String) queryParams.get(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString()));
+               }
+
+               return runTraversal(traversal).map(element -> {
+                       return element.asRelationship(); //Safe, since we are only operating on edges
+               });
+       }
+
+       @Override
+       public void executeDeletePartition(ChampPartition graph) {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               final Object[] objectIds = graph.getChampObjects()
+                               .stream()
+                               .filter(o -> o.getKey().isPresent())
+                               .map(o -> { return o.getKey().get(); })
+                               .collect(Collectors.toList())
+                               .toArray();
+
+               final Object[] relationshipIds = graph.getChampRelationships()
+                               .stream()
+                               .filter(o -> o.getKey().isPresent())
+                               .map(o -> { return o.getKey().get(); })
+                               .collect(Collectors.toList())
+                               .toArray();
+
+               runTraversal(startTraversal().V(objectIds).drop());
+               runTraversal(startTraversal().E(relationshipIds).drop());
+       }
+
+       @Override
+       protected ChampObject doStoreObject(ChampObject object)
+                       throws ChampMarshallingException, ChampObjectNotExistsException {
+               final GraphTraversal<Vertex, Vertex> traversal;
+
+               if (object.getKey().isPresent()) {
+                       traversal = startTraversal().V(object.getKey().get());
+               } else {
+                       traversal = startTraversal().addV(object.getType());
+               }
+
+               for (Entry<String, Object> property : object.getProperties().entrySet()) {
+
+                       if (property.getValue() instanceof List) {
+                               for (Object subPropertyValue : (List<?>) property.getValue()) {
+                                       traversal.property(VertexProperty.Cardinality.list, property.getKey(), subPropertyValue);
+                               }
+                       } else if (property.getValue() instanceof Set) {
+                               for (Object subPropertyValue : (Set<?>) property.getValue()) {
+                                       traversal.property(VertexProperty.Cardinality.set, property.getKey(), subPropertyValue);
+                               }
+                       } else {
+                               traversal.property(property.getKey(), property.getValue());
+                       }
+               }
+
+               return runTraversal(traversal).findFirst().get().asObject(); //TODO check if this return the updated vertices
+       }
+       
+       @Override
+       protected ChampObject doReplaceObject(ChampObject object)
+                       throws ChampMarshallingException, ChampObjectNotExistsException {
+               //TODO: implement the replace method when required
+               throw new UnsupportedOperationException("Method not implemented");
+       }
+       
+       @Override
+       protected ChampRelationship doReplaceRelationship(ChampRelationship relationship) throws ChampRelationshipNotExistsException, ChampMarshallingException {
+               //TODO: implement the replace method when required
+               throw new UnsupportedOperationException("Method not implemented");
+       }
+
+       @Override
+       protected ChampRelationship doStoreRelationship(ChampRelationship relationship) throws ChampObjectNotExistsException, ChampRelationshipNotExistsException, ChampMarshallingException {
+
+               /* FIXME: Only compatible with Tinkerpop 3.2.3 (Titan uses 3.0.1-incubating).
+
+               final GraphTraversal<?, Vertex> sourceBuilder;
+
+               if (relationship.getSource().getKey().isPresent()) {
+                       sourceBuilder = startTraversal().V(relationship.getSource().getKey().get()).as("source");
+               } else {
+                       sourceBuilder = startTraversal().addV(relationship.getSource().getType());
+               }
+
+               for (Entry<String, Object> sourceProperty : relationship.getSource().getProperties().entrySet()) {
+                       sourceBuilder.property(sourceProperty.getKey(), sourceProperty.getValue());
+               }
+
+               final GraphTraversal<?, Vertex> targetBuilder;
+
+               if (relationship.getTarget().getKey().isPresent()) {
+                       targetBuilder = sourceBuilder.V(relationship.getTarget().getKey().get()).as("target");
+               } else {
+                       targetBuilder = sourceBuilder.addV(relationship.getTarget().getType());
+               }
+
+               for (Entry<String, Object> targetProperty : relationship.getTarget().getProperties().entrySet()) {
+                       targetBuilder.property(targetProperty.getKey(), targetProperty.getValue());
+               }
+
+               final GraphTraversal<?, Edge> edgeBuilder = targetBuilder.addE(relationship.getType()).from("source");
+
+               for (Entry<String, Object> property : relationship.getProperties().entrySet()) {
+                       edgeBuilder.property(property.getKey(), property.getValue());
+               }
+
+               return runTraversal(edgeBuilder).filter(e -> e.isRelationship()).findFirst().get().asRelationship();
+               */
+
+               throw new UnsupportedOperationException("Cannot store relationships because of project setup (Incompatible Tinkerpop version in use)");
+       }
+
+       @Override
+       protected ChampPartition doStorePartition(ChampPartition submittedPartition) throws ChampObjectNotExistsException, ChampMarshallingException, ChampRelationshipNotExistsException {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               try {
+                       final HashMap<ChampObject, ChampObject> objectsWithKeys = new HashMap<ChampObject, ChampObject> ();
+                       final CreateChampPartitionable storedPartition = ChampPartition.create();
+
+                       for (ChampObject champObject : submittedPartition.getChampObjects()) {
+
+                               final ChampObject objectWithKey = doStoreObject(champObject);
+                               objectsWithKeys.put(champObject, objectWithKey);
+                       }
+
+                       for (ChampRelationship champRelationship : submittedPartition.getChampRelationships()) {
+                               if (!objectsWithKeys.containsKey(champRelationship.getSource())) {
+                                       final ChampObject objectWithKey = doStoreObject(champRelationship.getSource());
+
+                                       objectsWithKeys.put(champRelationship.getSource(), objectWithKey);
+                               }
+
+                               if (!objectsWithKeys.containsKey(champRelationship.getTarget())) {
+                                       final ChampObject objectWithKey = doStoreObject(champRelationship.getTarget());
+
+                                       objectsWithKeys.put(champRelationship.getTarget(), objectWithKey);
+                               }
+
+                               final ChampRelationship.Builder relWithKeysBuilder = new ChampRelationship.Builder(objectsWithKeys.get(champRelationship.getSource()),
+                                                                                                                                                                                       objectsWithKeys.get(champRelationship.getTarget()),
+                                                                                                                                                                                       champRelationship.getType());
+
+                               if (champRelationship.getKey().isPresent()) relWithKeysBuilder.key(champRelationship.getKey().get());
+                               
+                               relWithKeysBuilder.properties(champRelationship.getProperties());
+
+                               final ChampRelationship relationship = doStoreRelationship(relWithKeysBuilder.build());
+
+                               storedPartition.withRelationship(relationship);
+                       }
+
+                       for (ChampObject object : objectsWithKeys.values()) {
+                               storedPartition.withObject(object);
+                       }
+
+                       return storedPartition.build();
+               } catch (ChampObjectNotExistsException | ChampMarshallingException e) {
+                       throw e;
+               }
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/graph/impl/AbstractTinkerpopChampGraph.java b/src/main/java/org/openecomp/aai/champ/graph/impl/AbstractTinkerpopChampGraph.java
new file mode 100644 (file)
index 0000000..8128ec6
--- /dev/null
@@ -0,0 +1,753 @@
+/**
+ * ============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.openecomp.aai.champ.graph.impl;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.concurrent.atomic.AtomicBoolean;
+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.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.openecomp.aai.champ.event.ChampEvent;
+import org.openecomp.aai.champ.event.ChampEvent.ChampOperation;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.graph.impl.TitanChampGraphImpl.Builder;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.partition.CreateChampPartitionable;
+import org.openecomp.aai.champ.transform.TinkerpopChampformer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public abstract class AbstractTinkerpopChampGraph extends AbstractValidatingChampGraph {
+       
+       private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTinkerpopChampGraph.class);
+       private static final TinkerpopChampformer TINKERPOP_CHAMPFORMER = new TinkerpopChampformer();
+       private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+       private static final int COMMIT_RETRY_COUNT = 3;
+
+       private volatile AtomicBoolean isShutdown;
+
+       protected AbstractTinkerpopChampGraph(Map<String, Object> properties) {
+         super(properties);
+         
+         isShutdown = new AtomicBoolean(false);
+      Runtime.getRuntime().addShutdownHook(shutdownHook);
+       }
+       
+       private static final TinkerpopChampformer getChampformer() {
+               return TINKERPOP_CHAMPFORMER;
+       }
+
+       private static final ObjectMapper getObjectMapper() {
+               return OBJECT_MAPPER;
+       }
+
+       private Vertex writeVertex(ChampObject object) throws ChampObjectNotExistsException, ChampMarshallingException {
+               final Vertex vertex;
+               
+               if (object.getKey().isPresent()) {
+                       final Iterator<Vertex> vertexIter = getGraph().vertices(object.getKey().get());
+
+                       if (vertexIter.hasNext()) {
+                               vertex = vertexIter.next();
+                       } else throw new ChampObjectNotExistsException();
+               } else {
+                       vertex = getGraph().addVertex(object.getType());
+               }
+
+               for (Entry<String, Object> property : object.getProperties().entrySet()) {
+
+                       if (property.getValue() instanceof List) {
+                               for (Object subPropertyValue : (List<?>) property.getValue()) {
+                                       vertex.property(VertexProperty.Cardinality.list, property.getKey(), subPropertyValue);
+                               }
+                       } else if (property.getValue() instanceof Set) {
+                               for (Object subPropertyValue : (Set<?>) property.getValue()) {
+                                       vertex.property(VertexProperty.Cardinality.set, property.getKey(), subPropertyValue);
+                               }
+                       } else {
+                               vertex.property(property.getKey(), property.getValue());
+                       }
+               }
+
+               return vertex;
+       }
+       
+       private Vertex replaceVertex(ChampObject object) throws ChampObjectNotExistsException, ChampMarshallingException {
+               Vertex vertex;
+               
+               if (object.getKey().isPresent()) {
+                       final Iterator<Vertex> vertexIter = getGraph().vertices(object.getKey().get());
+
+                       if (vertexIter.hasNext()) {
+                               vertex = vertexIter.next();
+                       } else throw new ChampObjectNotExistsException();
+               } else {
+                       throw new ChampObjectNotExistsException();
+               }
+
+               //clear all the existing properties
+               Iterator<VertexProperty<Object>> it = vertex.properties();
+               while (it.hasNext()) {
+                       it.next().remove();
+               }
+               
+               for (Entry<String, Object> property : object.getProperties().entrySet()) {
+
+                       if (property.getValue() instanceof List) {
+                               for (Object subPropertyValue : (List<?>) property.getValue()) {
+                                       vertex.property(VertexProperty.Cardinality.list, property.getKey(), subPropertyValue);
+                               }
+                       } else if (property.getValue() instanceof Set) {
+                               for (Object subPropertyValue : (Set<?>) property.getValue()) {
+                                       vertex.property(VertexProperty.Cardinality.set, property.getKey(), subPropertyValue);
+                               }
+                       } else {
+                               vertex.property(property.getKey(), property.getValue());                                
+                       }
+               }
+
+               return vertex;
+       }
+
+       private Edge writeEdge(ChampRelationship relationship) throws ChampObjectNotExistsException, ChampRelationshipNotExistsException, ChampMarshallingException {
+
+               final Vertex source = writeVertex(relationship.getSource());
+               final Vertex target = writeVertex(relationship.getTarget());
+               final Edge edge;
+
+               if (relationship.getKey().isPresent()) {
+                       final Iterator<Edge> edgeIter = getGraph().edges(relationship.getKey().get());
+
+                       if (edgeIter.hasNext()) {
+                               edge = edgeIter.next();
+                       } else throw new ChampRelationshipNotExistsException();
+               } else {
+                       edge = source.addEdge(relationship.getType(), target);
+               }
+
+               for (Entry<String, Object> property : relationship.getProperties().entrySet()) {
+                       edge.property(property.getKey(), property.getValue());
+               }
+
+               return edge;
+       }
+       
+       private Edge replaceEdge(ChampRelationship relationship) throws  ChampRelationshipNotExistsException, ChampMarshallingException {
+               final Edge edge;
+               
+               if(!relationship.getSource().getKey().isPresent() || !relationship.getTarget().getKey().isPresent()){
+                       throw new IllegalArgumentException("Invalid source/target");
+               }
+               
+               if (relationship.getKey().isPresent()) {
+                       final Iterator<Edge> edgeIter = getGraph().edges(relationship.getKey().get());
+
+                       if (edgeIter.hasNext()) {
+                               edge = edgeIter.next();
+                               //validate if the source/target are the same as before. Throw error if not the same
+                               if (!edge.outVertex().id().equals(relationship.getSource().getKey().get())
+                                               || !edge.inVertex().id().equals(relationship.getTarget().getKey().get())) {
+                                       throw new IllegalArgumentException("source/target can't be updated");
+                               }
+
+                       } else throw new ChampRelationshipNotExistsException();
+               } else {
+                       throw new ChampRelationshipNotExistsException();
+               }
+               
+               // clear all the existing properties
+               Iterator<Property<Object>> it = edge.properties();
+               while (it.hasNext()) {
+                       it.next().remove();
+               }
+                               
+               for (Entry<String, Object> property : relationship.getProperties().entrySet()) {
+                       edge.property(property.getKey(), property.getValue());
+               }
+
+               return edge;
+       }
+
+       private void tryRollback() {
+               if (getGraph().features().graph().supportsTransactions()) {
+                       getGraph().tx().rollback();
+               }
+       }
+
+       private void tryCommit() {
+
+               if (getGraph().features().graph().supportsTransactions()) {
+
+                       final long initialBackoff = (int) (Math.random() * 50);
+
+                       for (int i = 0; i < COMMIT_RETRY_COUNT; i++) {
+                               try {
+                                       getGraph().tx().commit();
+                                       return;
+                               } catch (Throwable e) {
+                                       if (i == COMMIT_RETRY_COUNT - 1) {
+                                               LOGGER.error("Maxed out commit attempt retries, client must handle exception and retry", e);
+                                               getGraph().tx().rollback();
+                                               throw e;
+                                       }
+
+                                       final long backoff = (long) Math.pow(2, i) * initialBackoff;
+                                       LOGGER.warn("Caught exception while retrying transaction commit, retrying in " + backoff + " ms");
+                                       
+                                       try {
+                                               Thread.sleep(backoff);
+                                       } catch (InterruptedException ie) {
+                                               LOGGER.info("Interrupted while backing off on transaction commit");
+                                               return;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       protected abstract Graph getGraph();
+
+       private Thread shutdownHook = new Thread() {
+               @Override
+               public void run() {
+                       try {
+                               shutdown();
+                       } catch (IllegalStateException e) {
+                               //Suppress, because shutdown() has already been called
+                       }
+               }
+       };
+
+       protected boolean isShutdown() {
+               return isShutdown.get();
+       }
+
+       @Override
+       public Stream<ChampObject> queryObjects(Map<String, Object> queryParams) {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+       
+               //If they provided the object key, do this the quick way rather than creating a traversal
+               if (queryParams.containsKey(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_KEY.toString())) {
+                       try {
+                               final Optional<ChampObject> object = retrieveObject(queryParams.get(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_KEY.toString()));
+                       
+                               if (object.isPresent()) return Stream.of(object.get());
+                               else return Stream.empty();
+                       } catch (ChampUnmarshallingException e) {
+                               LOGGER.warn("Failed to unmarshall object", e);
+                               return Stream.empty();
+                       }
+               }
+
+               final GraphTraversal<Vertex, Vertex> query = getGraph().traversal().V();
+
+               for (Entry<String, Object> filter : queryParams.entrySet()) {
+                       if (filter.getKey().equals(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString())) {
+                               continue; //For performance reasons, the label is the last thing to be added
+                       } else {
+                               query.has(filter.getKey(), filter.getValue());
+                       }
+               }
+
+               if (queryParams.containsKey(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString())) {
+                       query.hasLabel((String) queryParams.get(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString()));
+               }
+
+               final Iterator<ChampObject> objIter = new Iterator<ChampObject> () {
+       
+                       private ChampObject next;
+
+
+                       @Override
+                       public boolean hasNext() {
+                               while (query.hasNext()) {
+                                       try {
+                                               next = getChampformer().unmarshallObject(query.next());
+                                               return true;
+                                       } catch (ChampUnmarshallingException e) {
+                                               LOGGER.warn("Failed to unmarshall tinkerpop vertex during query, returning partial results", e);
+                                       }                                       
+                               }
+
+                               tryCommit(); //Danger ahead if this iterator is not completely consumed
+                                                                                                               //then the transaction cache will hold stale values
+
+                               next = null;
+                               return false;
+                       }
+
+                       @Override
+                       public ChampObject next() {
+                               if (next == null) throw new NoSuchElementException();
+                               
+                               return next;
+                       }
+               };
+
+               return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
+                objIter, Spliterator.ORDERED | Spliterator.NONNULL), false);
+       }
+
+       @Override
+       public Optional<ChampObject> retrieveObject(Object key) throws ChampUnmarshallingException {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               final Iterator<Vertex> vertices = getGraph().vertices(key);
+               final Optional<ChampObject> optionalObject;
+
+               if (!vertices.hasNext()) optionalObject = Optional.empty();
+               else optionalObject = Optional.of(getChampformer().unmarshallObject(vertices.next()));
+
+               tryCommit();
+
+               return optionalObject;
+       }
+
+       @Override
+       public Stream<ChampRelationship> retrieveRelationships(ChampObject source) throws ChampUnmarshallingException, ChampObjectNotExistsException {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+               
+               final Vertex sourceVertex;
+
+               try {
+                       sourceVertex = getGraph().vertices(source.getKey().get()).next();
+               } catch (NoSuchElementException e) {                    
+                       tryRollback();
+
+                       throw new ChampObjectNotExistsException();
+               }
+
+               final Iterator<Edge> edges = sourceVertex.edges(Direction.BOTH);
+               final Iterator<ChampRelationship> relIter = new Iterator<ChampRelationship> () {
+
+                       private ChampRelationship next;
+
+                       @Override
+                       public boolean hasNext() {
+                               while (edges.hasNext()) {
+                                       try {
+                                               next = getChampformer().unmarshallRelationship(edges.next());
+                                               return true;
+                                       } catch (ChampUnmarshallingException e) {
+                                               LOGGER.warn("Failed to unmarshall tinkerpop edge during query, returning partial results", e);
+                                       }                                       
+                               }
+
+                               tryCommit();//Danger ahead if this iterator is not completely
+                                                                        //consumed, then the transaction cache will be stale
+                               next = null;
+                               return false;
+                       }
+
+                       @Override
+                       public ChampRelationship next() {
+                               if (next == null) throw new NoSuchElementException();
+                               
+                               return next;
+                       }
+               };
+
+               return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
+                relIter, Spliterator.ORDERED | Spliterator.NONNULL), false);
+       }
+
+       @Override
+       public ChampObject doStoreObject(ChampObject object) throws ChampMarshallingException, ChampObjectNotExistsException {
+
+               try {
+                       final Vertex vertex = writeVertex(object);
+
+                       tryCommit();
+                       
+                       return ChampObject.create()
+                                .from(object)
+                                .withKey(vertex.id())
+                                .build();
+                       
+               } catch (ChampObjectNotExistsException e) {
+                       tryRollback();
+
+                       throw e;
+               }
+       }
+       
+       @Override
+       public ChampObject doReplaceObject(ChampObject object) throws ChampMarshallingException, ChampObjectNotExistsException {
+
+               try {
+                       final Vertex vertex = replaceVertex(object);
+
+                       tryCommit();
+                       
+                       return ChampObject.create()
+                                  .from(object)
+                                  .withKey(vertex.id())
+                                  .build();
+                       
+               } catch (ChampObjectNotExistsException e) {
+                       tryRollback();
+
+                       throw e;
+               }
+       }
+
+       public void executeDeleteObject(Object key) throws ChampObjectNotExistsException {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               final Iterator<Vertex> vertex = getGraph().vertices(key);
+
+               if (!vertex.hasNext()) {
+                       tryRollback();
+
+                       throw new ChampObjectNotExistsException();
+               }
+
+               vertex.next().remove();
+               
+               tryCommit();
+       }
+
+       @Override
+       public ChampRelationship doStoreRelationship(ChampRelationship relationship)
+                       throws ChampUnmarshallingException, ChampObjectNotExistsException, ChampRelationshipNotExistsException, ChampMarshallingException  {
+
+               try {
+                       final Edge edge = writeEdge(relationship);
+
+                       tryCommit();
+       
+                       return getChampformer().unmarshallRelationship(edge);
+                       
+               } catch (ChampObjectNotExistsException | ChampRelationshipNotExistsException | ChampUnmarshallingException | ChampMarshallingException e) {
+                       tryRollback();
+
+                       throw e;
+               }
+       }
+       
+       @Override
+       public ChampRelationship doReplaceRelationship(ChampRelationship relationship)
+                       throws ChampUnmarshallingException, ChampRelationshipNotExistsException, ChampMarshallingException  {
+
+               try {
+                       final Edge edge = replaceEdge(relationship);
+
+                       tryCommit();
+       
+                       return getChampformer().unmarshallRelationship(edge);
+                       
+               } catch ( ChampRelationshipNotExistsException | ChampUnmarshallingException | ChampMarshallingException e) {
+                       tryRollback();
+
+                       throw e;
+               }
+       }
+
+       @Override
+       public Stream<ChampRelationship> queryRelationships(Map<String, Object> queryParams) {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               //If they provided the relationship key, do this the quick way rather than creating a traversal
+               if (queryParams.containsKey(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_KEY.toString())) {
+                       try {
+                               final Optional<ChampRelationship> relationship = retrieveRelationship(queryParams.get(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_KEY.toString()));
+                       
+                               if (relationship.isPresent()) return Stream.of(relationship.get());
+                               else return Stream.empty();
+                       } catch (ChampUnmarshallingException e) {
+                               LOGGER.warn("Failed to unmarshall relationship", e);
+                               return Stream.empty();
+                       }
+               }
+        
+               final GraphTraversal<Edge, Edge> query = getGraph().traversal().E();
+
+               for (Entry<String, Object> filter : queryParams.entrySet()) {
+                       if (filter.getKey().equals(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString())) {
+                               continue; //Add the label last for performance reasons
+                       } else {
+                               query.has(filter.getKey(), filter.getValue());
+                       }
+               }
+
+               if (queryParams.containsKey(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString())) {
+                       query.hasLabel((String) queryParams.get(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString()));
+               }
+
+               final Iterator<ChampRelationship> objIter = new Iterator<ChampRelationship> () {
+       
+                       private ChampRelationship next;
+
+
+                       @Override
+                       public boolean hasNext() {
+                               while (query.hasNext()) {
+                                       try {
+                                               next = getChampformer().unmarshallRelationship(query.next());
+                                               return true;
+                                       } catch (ChampUnmarshallingException e) {
+                                               LOGGER.warn("Failed to unmarshall tinkerpop vertex during query, returning partial results", e);
+                                       }                                       
+                               }
+
+                               tryCommit(); //Danger ahead if this iterator is not completely
+                                                                         //consumed, then the transaction cache will be stale
+                                       
+                               next = null;
+                               return false;
+                       }
+
+                       @Override
+                       public ChampRelationship next() {
+                               if (next == null) throw new NoSuchElementException();
+                               
+                               return next;
+                       }
+               };
+
+               return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
+                objIter, Spliterator.ORDERED | Spliterator.NONNULL), false);
+       }
+
+       @Override
+       public Optional<ChampRelationship> retrieveRelationship(Object key)
+                       throws ChampUnmarshallingException {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               final Iterator<Edge> edge = getGraph().edges(key);
+               final Optional<ChampRelationship> optionalRelationship;
+
+               if (!edge.hasNext()) optionalRelationship = Optional.empty();
+               else optionalRelationship = Optional.of(getChampformer().unmarshallRelationship(edge.next()));
+
+               tryCommit();
+
+               return optionalRelationship;
+       }
+
+       public void executeDeleteRelationship(ChampRelationship relationship) throws ChampRelationshipNotExistsException {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+               if (!relationship.getKey().isPresent()) throw new IllegalArgumentException("Key must be provided when deleting a relationship");
+
+               final Iterator<Edge> edge = getGraph().edges(relationship.getKey().get());
+               
+               if (!edge.hasNext()) {
+                       tryRollback();
+
+                       throw new ChampRelationshipNotExistsException();
+               }
+               
+               edge.next().remove();
+
+               tryCommit();
+       }
+
+       @Override
+       public ChampPartition doStorePartition(ChampPartition submittedPartition) throws ChampMarshallingException, ChampObjectNotExistsException, ChampRelationshipNotExistsException {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               try {
+                       final HashMap<ChampObject, ChampObject> objectsWithKeys = new HashMap<ChampObject, ChampObject> ();
+                       final CreateChampPartitionable storedPartition = ChampPartition.create();
+
+                       for (ChampObject champObject : submittedPartition.getChampObjects()) {
+                               final Vertex vertex = writeVertex(champObject);
+                               objectsWithKeys.put(champObject, ChampObject.create()
+                                                                                                                       .from(champObject)
+                                                                                                                       .withKey(vertex.id())
+                                                                                                                       .build());
+                       }
+
+                       for (ChampRelationship champRelationship : submittedPartition.getChampRelationships()) {
+                               if (!objectsWithKeys.containsKey(champRelationship.getSource())) {
+                                       final Vertex vertex = writeVertex(champRelationship.getSource());
+
+                                       objectsWithKeys.put(champRelationship.getSource(), ChampObject.create()
+                                                                                                               .from(champRelationship.getSource())
+                                                                                                               .withKey(vertex.id())
+                                                                                                               .build());
+                               }
+
+                               if (!objectsWithKeys.containsKey(champRelationship.getTarget())) {
+                                       final Vertex vertex = writeVertex(champRelationship.getTarget());
+
+                                       objectsWithKeys.put(champRelationship.getTarget(), ChampObject.create()
+                                                                                                               .from(champRelationship.getTarget())
+                                                                                                               .withKey(vertex.id())
+                                                                                                               .build());
+                               }
+
+                               final ChampRelationship.Builder relWithKeysBuilder = new ChampRelationship.Builder(objectsWithKeys.get(champRelationship.getSource()),
+                                                                                                                                                                                       objectsWithKeys.get(champRelationship.getTarget()),
+                                                                                                                                                                                       champRelationship.getType());
+
+                               if (champRelationship.getKey().isPresent()) relWithKeysBuilder.key(champRelationship.getKey().get());
+                               
+                               relWithKeysBuilder.properties(champRelationship.getProperties());
+
+                               final Edge edge = writeEdge(relWithKeysBuilder.build());
+
+                               storedPartition.withRelationship(ChampRelationship.create()
+                                                                                                                                       .from(champRelationship)
+                                                                                                                                       .withKey(edge.id())
+                                                                                                                                       .build());
+                       }
+
+                       for (ChampObject object : objectsWithKeys.values()) {
+                               storedPartition.withObject(object);
+                       }
+
+                       tryCommit();
+            
+                       return storedPartition.build();
+                       
+               } catch (ChampObjectNotExistsException | ChampMarshallingException e) {
+                       tryRollback();
+
+                       throw e;
+               }
+       }
+
+       public void executeDeletePartition(ChampPartition graph) {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               for (ChampObject champObject : graph.getChampObjects()) {
+                       try {
+                               final Object vertexId = champObject.getKey().get();
+                               final Iterator<Vertex> vertex = getGraph().vertices(vertexId);
+       
+                               if (vertex.hasNext()) {
+                                       vertex.next().remove();
+                               }
+                       } catch (NoSuchElementException e) {
+                               tryRollback();
+
+                               throw new IllegalArgumentException("Must pass a key to delete an object");
+                       }
+               }
+
+               for (ChampRelationship champRelationship : graph.getChampRelationships()) {
+                       try {
+                               final Iterator<Edge> edge = getGraph().edges(champRelationship.getKey().get());
+               
+                               if (edge.hasNext()) {
+                                       edge.next().remove();
+                               }
+                       } catch (NoSuchElementException e) {
+                               tryRollback();
+
+                               throw new IllegalArgumentException("Must pass a key to delete a relationship");
+                       }
+               }
+
+               tryCommit();
+
+       }
+
+       @Override
+       public void shutdown() {
+
+               if (isShutdown.compareAndSet(false, true)) {
+                 super.shutdown();
+                       try {
+                               getGraph().close();
+                       } catch (Throwable t) {
+                               LOGGER.error("Exception while shutting down graph", t);
+                       }
+               } else {
+                       throw new IllegalStateException("Cannot call shutdown() after shutdown() was already initiated");
+               }
+       }
+
+       @Override
+       public void storeSchema(ChampSchema schema) throws ChampSchemaViolationException {
+               if (isShutdown()) throw new IllegalStateException("Cannot call storeSchema() after shutdown has been initiated");
+
+               if (getGraph().features().graph().variables().supportsVariables()) {
+                       try {
+                               getGraph().variables().set("schema", getObjectMapper().writeValueAsBytes(schema));
+                       } catch (JsonProcessingException e) {
+                               throw new RuntimeException(e);
+                       }
+               } else {
+                       super.storeSchema(schema);
+               }
+       }
+
+       @Override
+       public ChampSchema retrieveSchema() {
+               if (isShutdown()) throw new IllegalStateException("Cannot call retrieveSchema() after shutdown has been initiated");
+
+               if (getGraph().features().graph().variables().supportsVariables()) {
+                       final Optional<byte[]> schema = getGraph().variables().get("schema");
+
+                       if (schema.isPresent()) {
+                               try {
+                                       return getObjectMapper().readValue(schema.get(), ChampSchema.class);
+                               } catch (IOException e) {
+                                       throw new RuntimeException(e);
+                               }
+                       }
+               }
+
+               return super.retrieveSchema();
+       }
+
+       @Override
+       public void deleteSchema() {
+               if (isShutdown()) throw new IllegalStateException("Cannot call deleteSchema() after shutdown has been initiated");
+
+               if (getGraph().features().graph().variables().supportsVariables()) {
+                       getGraph().variables().remove("schema");
+               } else {
+                       super.deleteSchema();
+               }
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/graph/impl/AbstractValidatingChampGraph.java b/src/main/java/org/openecomp/aai/champ/graph/impl/AbstractValidatingChampGraph.java
new file mode 100644 (file)
index 0000000..35d56f0
--- /dev/null
@@ -0,0 +1,176 @@
+/**
+ * ============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.openecomp.aai.champ.graph.impl;
+
+import java.util.Map;
+import java.util.Optional;
+
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.event.AbstractLoggingChampGraph;
+import org.openecomp.aai.champ.event.ChampEvent;
+import org.openecomp.aai.champ.event.ChampEvent.ChampOperation;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.graph.impl.TitanChampGraphImpl.Builder;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.schema.ChampSchemaEnforcer;
+
+public abstract class AbstractValidatingChampGraph extends AbstractLoggingChampGraph {
+
+       private ChampSchema schema = ChampSchema.emptySchema();
+
+       protected abstract ChampSchemaEnforcer getSchemaEnforcer();
+       protected abstract boolean isShutdown();
+
+       protected abstract ChampObject doReplaceObject(ChampObject object) throws ChampMarshallingException, ChampObjectNotExistsException;
+       protected abstract ChampObject doStoreObject(ChampObject object) throws ChampMarshallingException, ChampObjectNotExistsException;
+       protected abstract ChampRelationship doReplaceRelationship(ChampRelationship relationship) throws ChampUnmarshallingException, ChampRelationshipNotExistsException, ChampMarshallingException;
+       protected abstract ChampRelationship doStoreRelationship(ChampRelationship relationship) throws ChampUnmarshallingException, ChampObjectNotExistsException, ChampRelationshipNotExistsException, ChampMarshallingException;
+       protected abstract ChampPartition doStorePartition(ChampPartition partition) throws ChampRelationshipNotExistsException, ChampMarshallingException, ChampObjectNotExistsException;
+
+       protected AbstractValidatingChampGraph(Map<String, Object> properties) {
+         super(properties);
+       }
+       
+       public ChampObject executeStoreObject(ChampObject object)
+                       throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               validate(object);
+
+               return doStoreObject(object);
+       }
+       
+       public ChampObject executeReplaceObject(ChampObject object)
+                       throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               validate(object);
+
+               return doReplaceObject(object);
+       }
+
+       public ChampRelationship executeStoreRelationship(ChampRelationship relationship)
+                       throws ChampUnmarshallingException, ChampMarshallingException, ChampObjectNotExistsException, ChampSchemaViolationException, ChampRelationshipNotExistsException {      
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               validate(relationship);
+
+               return doStoreRelationship(relationship);
+       }
+       
+       public ChampRelationship executeReplaceRelationship(ChampRelationship relationship)
+                       throws ChampUnmarshallingException, ChampMarshallingException, ChampSchemaViolationException, ChampRelationshipNotExistsException {     
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               validate(relationship);
+
+               return doReplaceRelationship(relationship);
+       }
+
+       public ChampPartition executeStorePartition(ChampPartition partition) throws ChampSchemaViolationException, ChampRelationshipNotExistsException, ChampMarshallingException, ChampObjectNotExistsException {
+               if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+               validate(partition);
+
+               return doStorePartition(partition);
+       }
+
+       protected void validate(ChampObject object) throws ChampSchemaViolationException {
+               final Optional<ChampObjectConstraint> objectConstraint = retrieveSchema().getObjectConstraint(object.getType());
+
+               if (objectConstraint.isPresent()) getSchemaEnforcer().validate(object, objectConstraint.get());
+       }
+
+       protected void validate(ChampRelationship relationship) throws ChampSchemaViolationException {
+               final ChampSchema graphSchema = retrieveSchema();
+               final Optional<ChampRelationshipConstraint> relationshipConstraint = graphSchema.getRelationshipConstraint(relationship.getType());
+               final Optional<ChampObjectConstraint> sourceObjConstraint = graphSchema.getObjectConstraint(relationship.getSource().getType());
+               final Optional<ChampObjectConstraint> targetObjConstraint = graphSchema.getObjectConstraint(relationship.getTarget().getType());
+
+               if (relationshipConstraint.isPresent()) getSchemaEnforcer().validate(relationship, relationshipConstraint.get());
+               if (sourceObjConstraint.isPresent()) getSchemaEnforcer().validate(relationship.getSource(), sourceObjConstraint.get());
+               if (targetObjConstraint.isPresent()) getSchemaEnforcer().validate(relationship.getTarget(), targetObjConstraint.get());
+       }
+
+       protected void validate(ChampPartition partition) throws ChampSchemaViolationException {
+               for (ChampObject object : partition.getChampObjects()) {
+                       validate(object);
+               }
+
+               for (ChampRelationship relationship : partition.getChampRelationships()) {
+                       validate(relationship);
+               }
+       }
+
+       @Override
+       public void storeSchema(ChampSchema schema) throws ChampSchemaViolationException {
+               if (isShutdown()) throw new IllegalStateException("Cannot call storeSchema() after shutdown has been initiated");
+
+               this.schema = schema;
+       }
+
+       @Override
+       public ChampSchema retrieveSchema() {
+               if (isShutdown()) throw new IllegalStateException("Cannot call retrieveSchema() after shutdown has been initiated");
+
+               return schema;
+       }
+
+       @Override
+       public void updateSchema(ChampObjectConstraint objectConstraint) throws ChampSchemaViolationException {
+               if (isShutdown()) throw new IllegalStateException("Cannot call updateSchema() after shutdown has been initiated");
+
+               final ChampSchema currentSchema = retrieveSchema();
+               final ChampSchema updatedSchema = new ChampSchema.Builder(currentSchema)
+                                                                                               .constraint(objectConstraint)
+                                                                                               .build();
+               
+               storeSchema(updatedSchema);
+       }
+
+       @Override
+       public void updateSchema(ChampRelationshipConstraint relationshipConstraint) throws ChampSchemaViolationException {
+               if (isShutdown()) throw new IllegalStateException("Cannot call updateSchema() after shutdown has been initiated");
+
+               final ChampSchema currentSchema = retrieveSchema();
+               final ChampSchema updatedSchema = new ChampSchema.Builder(currentSchema)
+                                                                                               .constraint(relationshipConstraint)
+                                                                                               .build();
+               
+               storeSchema(updatedSchema);
+       }
+
+       @Override
+       public void deleteSchema() {
+               if (isShutdown()) throw new IllegalStateException("Cannot call deleteSchema() after shutdown has been initiated");
+               this.schema = ChampSchema.emptySchema();
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/graph/impl/ChampAPIImpl.java b/src/main/java/org/openecomp/aai/champ/graph/impl/ChampAPIImpl.java
new file mode 100644 (file)
index 0000000..34453fc
--- /dev/null
@@ -0,0 +1,87 @@
+/**
+ * ============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.openecomp.aai.champ.graph.impl;
+
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ChampAPIImpl implements ChampAPI {
+
+       private static final Logger LOGGER = LoggerFactory.getLogger(ChampAPIImpl.class);
+
+       private final AtomicBoolean shutdown;
+       private final ChampGraph.Type type;
+       private final ConcurrentHashMap<String, ChampGraph> graphs;
+
+       public ChampAPIImpl(ChampGraph.Type type) {
+               this.type = type;
+               this.graphs = new ConcurrentHashMap<String, ChampGraph> ();
+               this.shutdown = new AtomicBoolean(false);
+       }
+
+       private ConcurrentHashMap<String, ChampGraph> getGraphs() {
+               return graphs;
+       }
+
+       @Override
+       public ChampGraph getGraph(String graphName) {
+               if (shutdown.get()) throw new IllegalStateException("Cannot call getGraph() after shutdown() has been initiated");
+
+               if (getGraphs().containsKey(graphName)) return getGraphs().get(graphName);
+
+               final ChampGraph graph = ChampGraph.Factory.newInstance(type, graphName);
+               
+               final ChampGraph existingGraph = getGraphs().putIfAbsent(graphName, graph);
+               
+               if (existingGraph == null) return graph;
+               
+               return existingGraph;
+       }
+
+       @Override
+       public void shutdown() {
+               if (shutdown.compareAndSet(false, true)) {
+                       for (Entry<String, ChampGraph> graphEntry : graphs.entrySet()) {
+                               LOGGER.info("Shutting down graph {}", graphEntry.getKey());
+                               
+                               try {
+                                       graphEntry.getValue().shutdown();
+                                       LOGGER.info("Graph {} shutdown successfully", graphEntry.getKey());
+                               } catch (Throwable t) {
+                                       LOGGER.warn("Caught exception while shutting down graph " + graphEntry.getKey(), t);
+                               }
+                       }
+               }
+       }
+
+       @Override
+       public ChampGraph.Type getType() {
+               return type;
+       }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/graph/impl/InMemoryChampGraphImpl.java b/src/main/java/org/openecomp/aai/champ/graph/impl/InMemoryChampGraphImpl.java
new file mode 100644 (file)
index 0000000..ab59e64
--- /dev/null
@@ -0,0 +1,182 @@
+/**
+ * ============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.openecomp.aai.champ.graph.impl;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Stream;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
+import org.openecomp.aai.champ.ChampCapabilities;
+import org.openecomp.aai.champ.exceptions.ChampIndexNotExistsException;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+import org.openecomp.aai.champ.schema.ChampSchemaEnforcer;
+import org.openecomp.aai.champ.schema.DefaultChampSchemaEnforcer;
+import org.slf4j.Logger;
+
+import com.att.nsa.cambria.client.CambriaPublisher;
+
+public final class InMemoryChampGraphImpl extends AbstractTinkerpopChampGraph {
+
+       private static final ChampCapabilities CAPABILITIES = new ChampCapabilities() {
+
+               @Override
+               public boolean canDeleteObjectIndices() {
+                       return true;
+               }
+
+               @Override
+               public boolean canDeleteRelationshipIndices() {
+                       return true;
+               }
+       };
+
+       private final ConcurrentHashMap<String, ChampObjectIndex> objectIndices;
+       private final ConcurrentHashMap<String, ChampRelationshipIndex> relationshipIndices;
+
+       private final ChampSchemaEnforcer schemaEnforcer;
+       private final TinkerGraph graph;
+
+       private InMemoryChampGraphImpl(Builder builder) {
+           super(builder.graphConfiguration);
+               this.graph = TinkerGraph.open();
+       
+               this.objectIndices = new ConcurrentHashMap<String, ChampObjectIndex> ();
+               this.relationshipIndices = new ConcurrentHashMap<String, ChampRelationshipIndex> ();
+
+               this.schemaEnforcer = builder.schemaEnforcer;
+       }
+
+       public static class Builder {
+           private final Map<String, Object> graphConfiguration = new HashMap<String, Object> ();
+               private ChampSchemaEnforcer schemaEnforcer = new DefaultChampSchemaEnforcer();
+
+               public Builder() {}
+
+               public Builder schemaEnforcer(ChampSchemaEnforcer schemaEnforcer) {
+                       this.schemaEnforcer = schemaEnforcer;
+                       return this;
+               }
+
+               public InMemoryChampGraphImpl build() {
+                       return new InMemoryChampGraphImpl(this);
+               }
+       }
+
+       protected ChampSchemaEnforcer getSchemaEnforcer() {
+               return schemaEnforcer;
+       }
+
+       @Override
+       protected TinkerGraph getGraph() {
+               return graph;
+       }
+
+       private ConcurrentHashMap<String, ChampObjectIndex> getObjectIndices() {
+               return objectIndices;
+       }
+
+       private ConcurrentHashMap<String, ChampRelationshipIndex> getRelationshipIndices() {
+               return relationshipIndices;
+       }
+
+       public void executeStoreObjectIndex(ChampObjectIndex index) {
+               if (isShutdown()) throw new IllegalStateException("Cannot call storeObjectIndex() after shutdown has been initiated");
+
+               getGraph().createIndex(index.getField().getName(), Vertex.class);
+               getObjectIndices().put(index.getName(), index);
+       }
+
+       @Override
+       public Optional<ChampObjectIndex> retrieveObjectIndex(String indexName) {
+               if (isShutdown()) throw new IllegalStateException("Cannot call retrieveObjectIndex() after shutdown has been initiated");
+
+               if (getObjectIndices().containsKey(indexName))
+                       return Optional.of(getObjectIndices().get(indexName));
+                       
+               return Optional.empty();
+       }
+
+       @Override
+       public Stream<ChampObjectIndex> retrieveObjectIndices() {
+               if (isShutdown()) throw new IllegalStateException("Cannot call retrieveObjectIndices() after shutdown has been initiated");
+
+               return getObjectIndices().values().stream();
+       }
+
+       public void executeDeleteObjectIndex(String indexName) throws ChampIndexNotExistsException {
+               if (isShutdown()) throw new IllegalStateException("Cannot call deleteObjectIndex() after shutdown has been initiated");
+
+               final ChampObjectIndex objectIndex = getObjectIndices().remove(indexName);
+
+               if (objectIndex == null) throw new ChampIndexNotExistsException();
+
+               getGraph().dropIndex(objectIndex.getField().getName(), Vertex.class);
+       }
+
+       public void executeStoreRelationshipIndex(ChampRelationshipIndex index) {
+               if (isShutdown()) throw new IllegalStateException("Cannot call storeRelationshipIndex() after shutdown has been initiated");
+
+               getGraph().createIndex(index.getField().getName(), Edge.class);
+               getRelationshipIndices().put(index.getName(), index);
+       }
+
+       @Override
+       public Optional<ChampRelationshipIndex> retrieveRelationshipIndex(String indexName) {
+               if (isShutdown()) throw new IllegalStateException("Cannot call retrieveRelationshipIndex() after shutdown has been initiated");
+
+               if (getRelationshipIndices().containsKey(indexName)) {
+                       return Optional.of(getRelationshipIndices().get(indexName));
+               }
+               
+               return Optional.empty();
+       }
+
+       @Override
+       public Stream<ChampRelationshipIndex> retrieveRelationshipIndices() {
+               if (isShutdown()) throw new IllegalStateException("Cannot call retrieveRelationshipIndices() after shutdown has been initiated");
+
+               return getRelationshipIndices().values().stream();
+       }
+
+       public void executeDeleteRelationshipIndex(String indexName) throws ChampIndexNotExistsException {
+               if (isShutdown()) throw new IllegalStateException("Cannot call deleteRelationshipIndex() after shutdown has been initiated");
+
+               final ChampRelationshipIndex relationshipIndex = getRelationshipIndices().remove(indexName);
+
+               if (relationshipIndex == null) throw new ChampIndexNotExistsException();
+               
+               getGraph().dropIndex(relationshipIndex.getField().getName(), Edge.class);
+       }
+
+       @Override
+       public ChampCapabilities capabilities() {
+               return CAPABILITIES;
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/graph/impl/TitanChampGraphImpl.java b/src/main/java/org/openecomp/aai/champ/graph/impl/TitanChampGraphImpl.java
new file mode 100644 (file)
index 0000000..2bb8d2e
--- /dev/null
@@ -0,0 +1,461 @@
+/**
+ * ============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.openecomp.aai.champ.graph.impl;
+
+import java.time.temporal.ChronoUnit;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.openecomp.aai.champ.ChampCapabilities;
+import org.openecomp.aai.champ.exceptions.ChampIndexNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.model.ChampCardinality;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.ChampPropertyConstraint;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.schema.ChampSchemaEnforcer;
+import org.openecomp.aai.champ.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_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;
+
+       private 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());
+               }
+
+               final Object storageBackend = builder.graphConfiguration.get("storage.backend");
+
+               if (storageBackend.equals("cassandra") ||
+                       storageBackend.equals("cassandrathrift") ||
+                       storageBackend.equals("astyanax") ||
+                       storageBackend.equals("embeddedcassandra")) {
+                       titanGraphBuilder.set(TITAN_CASSANDRA_KEYSPACE, builder.graphName);
+               } else if (storageBackend.equals("hbase")) {
+                       titanGraphBuilder.set(TITAN_HBASE_TABLE, builder.graphName);
+               } else if (storageBackend.equals("berkleyje")) {
+                       throw new RuntimeException("storage.backend=berkleyje cannot handle multiple graphs on a single DB, not usable");
+               } else if (storageBackend.equals("inmemory")) {
+               } else {
+                       throw new RuntimeException("Unknown storage.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 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);
+               }
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/ie/Exporter.java b/src/main/java/org/openecomp/aai/champ/ie/Exporter.java
new file mode 100644 (file)
index 0000000..e897e2b
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * ============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.openecomp.aai.champ.ie;
+
+import java.io.OutputStream;
+
+import org.openecomp.aai.champ.ChampGraph;
+
+public interface Exporter {
+
+       public void exportData(ChampGraph graph, OutputStream os);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/ie/GraphMLImporterExporter.java b/src/main/java/org/openecomp/aai/champ/ie/GraphMLImporterExporter.java
new file mode 100644 (file)
index 0000000..05b4919
--- /dev/null
@@ -0,0 +1,460 @@
+/**
+ * ============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.openecomp.aai.champ.ie;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+public class GraphMLImporterExporter implements Importer, Exporter {
+
+       private static final Logger LOGGER = LoggerFactory.getLogger(GraphMLImporterExporter.class);
+
+       private static class GraphMLKey {
+               private final String id;
+               private final String attrName;
+               private final String attrType;
+
+               public GraphMLKey(String id, String attrName, Class<?> attrType) {
+                       this.id = id;
+                       this.attrName = attrName;
+
+                       if (attrType.equals(Boolean.class)) {
+                               this.attrType = "boolean";
+                       } else if (attrType.equals(Integer.class)) {
+                               this.attrType = "int";
+                       } else if (attrType.equals(Long.class)) {
+                               this.attrType = "long";
+                       } else if (attrType.equals(Float.class)) {
+                               this.attrType = "float";
+                       } else if (attrType.equals(Double.class)) {
+                               this.attrType = "double";
+                       } else if (attrType.equals(String.class)) {
+                               this.attrType = "string";
+                       } else {
+                               throw new RuntimeException("Cannot handle type " + attrType + " in GraphML");
+                       }
+               }
+       }
+
+       public void importData(ChampAPI api, InputStream is) {
+
+               try {
+                       final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+                       final DocumentBuilder builder = factory.newDocumentBuilder();
+                       final InputSource inputSource = new InputSource(is);
+                   final Document doc = builder.parse(inputSource);
+
+                       final Map<String, Map<String, String>> nodePropertyDefinitions = new HashMap<String, Map<String, String>> ();
+                       final Map<String, Map<String, String>> edgePropertyDefinitions = new HashMap<String, Map<String, String>> ();
+                       final Set<Map<String, String>> nodeDefaults = new HashSet<Map<String, String>> ();
+                       final Set<Map<String, String>> edgeDefaults = new HashSet<Map<String, String>> ();
+
+                       final NodeList keys = doc.getElementsByTagName("key");
+
+                       for (int i = 0; i < keys.getLength(); i++) {
+                               final Node key = keys.item(i);
+                               final String id = key.getAttributes().getNamedItem("id").getNodeValue();
+                               final String attrName = key.getAttributes().getNamedItem("attr.name").getNodeValue();
+                               final String attrType = key.getAttributes().getNamedItem("attr.type").getNodeValue();
+                               final String elementType = key.getAttributes().getNamedItem("for").getNodeValue();
+                               final Map<String, String> propertyDefinitions = new HashMap<String, String> ();
+
+                               propertyDefinitions.put("attr.name", attrName);
+                               propertyDefinitions.put("attr.type",  attrType);
+
+                               final NodeList keyChildren = key.getChildNodes();
+
+                               for (int j = 0; j < keyChildren.getLength(); j++) {
+                                       final Node keyChild = keyChildren.item(j);
+
+                                       if (keyChild.getNodeType() != Node.ELEMENT_NODE) continue;
+
+                                       if (keyChild.getNodeName().equals("default")) {
+                                               propertyDefinitions.put("default", keyChild.getFirstChild().getNodeValue());
+
+                                               if (elementType.equals("node")) nodeDefaults.add(propertyDefinitions);
+                                               else if (elementType.equals("edge")) edgeDefaults.add(propertyDefinitions);
+                                       }
+                               }
+
+                               if (elementType.equals("node")) {
+                                       nodePropertyDefinitions.put(id, propertyDefinitions);
+                               } else if (elementType.equals("edge")) {
+                                       edgePropertyDefinitions.put(id, propertyDefinitions);
+                               } else {
+                                       LOGGER.warn("Unknown element type {}, skipping", elementType);
+                               }
+                       }
+
+                       final NodeList graphs = doc.getElementsByTagName("graph");
+
+                       for (int i = 0; i < graphs.getLength(); i++) {
+                               final Node graph = graphs.item(i);
+                               final String graphName = graph.getAttributes().getNamedItem("id").getNodeValue();
+                               final NodeList nodesAndEdges = graph.getChildNodes();
+
+                               api.getGraph(graphName).storeObjectIndex(ChampObjectIndex.create()
+                                                                                                                                                       .ofName("importAssignedId")
+                                                                                                                                                       .onAnyType()
+                                                                                                                                                       .forField("importAssignedId")
+                                                                                                                                                       .build());
+
+                               for (int j = 0; j < nodesAndEdges.getLength(); j++) {
+                                       final Node nodeOrEdge = nodesAndEdges.item(j);
+
+                                       if (nodeOrEdge.getNodeType() != Node.ELEMENT_NODE) continue;
+
+                                       if (nodeOrEdge.getNodeName().equals("node")) {
+                                               writeNode(api.getGraph(graphName), nodeOrEdge, nodePropertyDefinitions, nodeDefaults);
+                                       } else if (nodeOrEdge.getNodeName().equals("edge")) {
+                                               writeEdge(api.getGraph(graphName), nodeOrEdge, edgePropertyDefinitions, edgeDefaults);
+                                       } else {
+                                               LOGGER.warn("Unknown object {} found in graphML, skipping", nodeOrEdge.getNodeName());
+                                       }
+                               }
+                       }
+               } catch (ParserConfigurationException e) {
+                       throw new RuntimeException("Failed to setup DocumentBuilder", e);
+               } catch (SAXException e) {
+                       throw new RuntimeException("Failed to parse input stream", e);
+               } catch (IOException e) {
+                       throw new RuntimeException("Failed to parse input stream", e);
+               }
+       }
+
+       private void writeEdge(ChampGraph graph, Node edge, Map<String, Map<String, String>> edgePropertyDefinitions, Set<Map<String, String>> edgeDefaults) {
+               final NamedNodeMap edgeAttributes = edge.getAttributes();
+               final NodeList data = edge.getChildNodes();
+               final Object sourceKey = edgeAttributes.getNamedItem("source").getNodeValue();
+               final Object targetKey = edgeAttributes.getNamedItem("target").getNodeValue();
+               final ChampObject sourceObject;
+               final ChampObject targetObject;
+
+               try {
+                       final Optional<ChampObject> source = graph.queryObjects(Collections.singletonMap("importAssignedId", sourceKey)).findFirst();
+                       final Optional<ChampObject> target = graph.queryObjects(Collections.singletonMap("importAssignedId", targetKey)).findFirst();
+
+                       if (!source.isPresent()) {
+                               sourceObject = graph.storeObject(ChampObject.create()
+                                                                                                               .ofType("undefined")
+                                                                                                               .withoutKey()
+                                                                                                               .build());
+                       } else sourceObject = source.get();
+       
+                       if (!target.isPresent()) {
+                               targetObject = graph.storeObject(ChampObject.create()
+                                                                                                               .ofType("undefined")
+                                                                                                               .withoutKey()
+                                                                                                               .build());
+                       } else targetObject = target.get();
+
+               } catch (ChampMarshallingException e) {
+                       LOGGER.error("Failed to marshall object to backend type, skipping this edge", e);
+                       return;
+               } catch (ChampSchemaViolationException e) {
+                       LOGGER.error("Source/target object violates schema constraint(s)", e);
+                       return;
+               } catch (ChampObjectNotExistsException e) {
+                       LOGGER.error("Failed to update existing source/target ChampObject", e);
+                       return;
+               }
+
+               final ChampRelationship.Builder champRelBuilder = new ChampRelationship.Builder(sourceObject, targetObject, "undefined");
+
+               for (Map<String, String> defaultProperty : edgeDefaults) {
+                       champRelBuilder.property(defaultProperty.get("attr.name"), defaultProperty.get("default"));
+               }
+
+               for (int k = 0; k < data.getLength(); k++) {
+                       final Node datum = data.item(k);
+
+                       if (datum.getNodeType() != Node.ELEMENT_NODE) continue;
+
+                       final String nodeProperty = datum.getAttributes().getNamedItem("key").getNodeValue();
+                       final Map<String, String> nodePropertyDefinition = edgePropertyDefinitions.get(nodeProperty);
+
+                       switch (nodePropertyDefinition.get("attr.type")) {
+                       case "boolean":
+                               champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Boolean.valueOf(datum.getFirstChild().getNodeValue()));
+                               break;
+                       case "int":
+                               champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Integer.valueOf(datum.getFirstChild().getNodeValue()));
+                               break;
+                       case "long":
+                               champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Long.valueOf(datum.getFirstChild().getNodeValue()));
+                               break;
+                       case "float":
+                               champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Float.valueOf(datum.getFirstChild().getNodeValue()));
+                               break;
+                       case "double":
+                               champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Double.valueOf(datum.getFirstChild().getNodeValue()));
+                               break;
+                       case "string":
+                               champRelBuilder.property(nodePropertyDefinition.get("attr.name"), datum.getFirstChild().getNodeValue());
+                               break;
+                       default:
+                               throw new RuntimeException("Unknown node property attr.type " + nodePropertyDefinition.get("attr.type"));
+                       }
+               }
+
+               final ChampRelationship relToStore = champRelBuilder.build();
+
+               try {
+                       graph.storeRelationship(relToStore);
+               } catch (ChampMarshallingException e) {
+                       LOGGER.warn("Failed to marshall ChampObject to backend type", e);
+               } catch (ChampSchemaViolationException e) {
+                       LOGGER.error("Failed to store object (schema violated): " + relToStore, e);
+               } catch (ChampRelationshipNotExistsException e) {
+                       LOGGER.error("Failed to update existing ChampRelationship", e);
+               } catch (ChampObjectNotExistsException e) {
+                       LOGGER.error("Objects bound to relationship do not exist (should never happen)");
+               } catch (ChampUnmarshallingException e) {
+                       LOGGER.error("Failed to unmarshall ChampObject to backend type");
+               }
+       }
+
+       private void writeNode(ChampGraph graph, Node node, Map<String, Map<String, String>> nodePropertyDefinitions, Set<Map<String, String>> nodeDefaults) {
+               final NamedNodeMap nodeAttributes = node.getAttributes();
+               final Object importAssignedId = nodeAttributes.getNamedItem("id").getNodeValue();
+               final NodeList data = node.getChildNodes();
+               final Map<String, Object> properties = new HashMap<String, Object> ();
+
+               for (int k = 0; k < data.getLength(); k++) {
+                       final Node datum = data.item(k);
+
+                       if (datum.getNodeType() != Node.ELEMENT_NODE) continue;
+
+                       final String nodeProperty = datum.getAttributes().getNamedItem("key").getNodeValue();
+                       final Map<String, String> nodePropertyDefinition = nodePropertyDefinitions.get(nodeProperty);
+
+                       switch (nodePropertyDefinition.get("attr.type")) {
+                       case "boolean":
+                               properties.put(nodePropertyDefinition.get("attr.name"), Boolean.valueOf(datum.getFirstChild().getNodeValue()));
+                               break;
+                       case "int":
+                               properties.put(nodePropertyDefinition.get("attr.name"), Integer.valueOf(datum.getFirstChild().getNodeValue()));
+                               break;
+                       case "long":
+                               properties.put(nodePropertyDefinition.get("attr.name"), Long.valueOf(datum.getFirstChild().getNodeValue()));
+                               break;
+                       case "float":
+                               properties.put(nodePropertyDefinition.get("attr.name"), Float.valueOf(datum.getFirstChild().getNodeValue()));
+                               break;
+                       case "double":
+                               properties.put(nodePropertyDefinition.get("attr.name"), Double.valueOf(datum.getFirstChild().getNodeValue()));
+                               break;
+                       case "string":
+                               properties.put(nodePropertyDefinition.get("attr.name"), datum.getFirstChild().getNodeValue());
+                               break;
+                       default:
+                               throw new RuntimeException("Unknown node property attr.type " + nodePropertyDefinition.get("attr.type"));
+                       }
+               }
+
+               if (!properties.containsKey("type")) throw new RuntimeException("No type provided for object (was this GraphML exported by Champ?)");
+
+               final ChampObject.Builder champObjBuilder = new ChampObject.Builder((String) properties.get("type"));
+
+               for (Map<String, String> defaultProperty : nodeDefaults) {
+                       champObjBuilder.property(defaultProperty.get("attr.name"), defaultProperty.get("default"));
+               }
+
+               properties.remove("type");
+
+               champObjBuilder.properties(properties)
+                                               .property("importAssignedId", importAssignedId);
+
+               final ChampObject objectToStore = champObjBuilder.build();
+
+               try {
+                       graph.storeObject(objectToStore);
+               } catch (ChampMarshallingException e) {
+                       LOGGER.warn("Failed to marshall ChampObject to backend type", e);
+               } catch (ChampSchemaViolationException e) {
+                       LOGGER.error("Failed to store object (schema violated): " + objectToStore, e);
+               } catch (ChampObjectNotExistsException e) {
+                       LOGGER.error("Failed to update existing ChampObject", e);
+               }
+       }
+
+       @Override
+       public void exportData(ChampGraph graph, OutputStream os) {
+
+               final XMLOutputFactory output = XMLOutputFactory.newInstance();
+
+               try {
+                       final XMLStreamWriter writer = output.createXMLStreamWriter(os);
+
+                       writer.writeStartDocument();
+                       writer.writeStartElement("graphml");
+                       writer.writeDefaultNamespace("http://graphml.graphdrawing.org/xmlns");
+                       writer.writeNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
+                       writer.writeAttribute("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation", "http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd");
+
+                       final List<ChampObject> nodes = new LinkedList<ChampObject> ();
+                       final List<ChampRelationship> edges = new LinkedList<ChampRelationship> ();
+                       final Map<String, GraphMLKey> nodeKeys = new HashMap<String, GraphMLKey> ();
+                       final Map<String, GraphMLKey> edgeKeys = new HashMap<String, GraphMLKey> ();
+                       final AtomicInteger elementCount = new AtomicInteger();
+
+                       graph.queryObjects(Collections.emptyMap()).forEach(object -> {
+                               nodes.add(object);
+
+                               for (Map.Entry<String, Object> property : object.getProperties().entrySet()) {
+                                       if (nodeKeys.containsKey(property.getKey())) continue;
+
+                                       nodeKeys.put(property.getKey(), new GraphMLKey("d" + elementCount.incrementAndGet(), property.getKey(), property.getValue().getClass()));
+                               }
+
+                               nodeKeys.put("type", new GraphMLKey("d" + elementCount.incrementAndGet(), "type", String.class));
+                       });
+
+                       graph.queryRelationships(Collections.emptyMap()).forEach(relationship -> {
+                               edges.add(relationship);
+
+                               for (Map.Entry<String, Object> property : relationship.getProperties().entrySet()) {
+                                       if (nodeKeys.containsKey(property.getKey())) continue;
+
+                                       edgeKeys.put(property.getKey(), new GraphMLKey("d" + elementCount.incrementAndGet(), property.getKey(), property.getValue().getClass()));
+                               }
+
+                               edgeKeys.put("type", new GraphMLKey("d" + elementCount.incrementAndGet(), "type", String.class));
+                       });
+
+                       for (Entry<String, GraphMLKey> nodeKey : nodeKeys.entrySet()) {
+                               final GraphMLKey graphMlKey = nodeKey.getValue();
+
+                               writer.writeStartElement("key");
+                               writer.writeAttribute("id", graphMlKey.id);
+                               writer.writeAttribute("for", "node");
+                               writer.writeAttribute("attr.name", graphMlKey.attrName);
+                               writer.writeAttribute("attr.type", graphMlKey.attrType);
+                               writer.writeEndElement();
+                       }
+
+                       for (Entry<String, GraphMLKey> edgeKey : edgeKeys.entrySet()) {
+                               final GraphMLKey graphMlKey = edgeKey.getValue();
+
+                               writer.writeStartElement("key");
+                               writer.writeAttribute("id", graphMlKey.id);
+                               writer.writeAttribute("for", "edge");
+                               writer.writeAttribute("attr.name", graphMlKey.attrName);
+                               writer.writeAttribute("attr.type", graphMlKey.attrType);
+                               writer.writeEndElement();
+                       }
+
+                       for (ChampObject object : nodes) {
+                               try {
+                                       writer.writeStartElement("node");
+                                       writer.writeAttribute("id", String.valueOf(object.getKey().get()));
+
+                                       writer.writeStartElement("data");
+                                       writer.writeAttribute("key", nodeKeys.get("type").id);
+                                       writer.writeCharacters(object.getType());
+                                       writer.writeEndElement();
+
+                                       for (Entry<String, Object> property : object.getProperties().entrySet()) {
+                                               final GraphMLKey key = nodeKeys.get(property.getKey());
+
+                                               writer.writeStartElement("data");
+                                               writer.writeAttribute("key", key.id);
+                                               writer.writeCharacters(String.valueOf(property.getValue()));
+                                               writer.writeEndElement();
+                                       }
+
+                                       writer.writeEndElement();
+                               } catch (XMLStreamException e) {
+                                       throw new RuntimeException("Failed to write edge to output stream", e);
+                               }
+                       }
+
+                       for (ChampRelationship relationship : edges) {
+                               try {
+                                       writer.writeStartElement("edge");
+                                       writer.writeAttribute("id", String.valueOf(relationship.getKey().get()));
+
+                                       writer.writeStartElement("data");
+                                       writer.writeAttribute("key", edgeKeys.get("type").id);
+                                       writer.writeCharacters(relationship.getType());
+                                       writer.writeEndElement();
+
+                                       for (Entry<String, Object> property : relationship.getProperties().entrySet()) {
+                                               final GraphMLKey key = edgeKeys.get(property.getKey());
+
+                                               writer.writeStartElement("data");
+                                               writer.writeAttribute("key", key.id);
+                                               writer.writeCharacters(String.valueOf(property.getValue()));
+                                               writer.writeEndElement();
+                                       }
+
+                                       writer.writeEndElement();
+                               } catch (XMLStreamException e) {
+                                       throw new RuntimeException("Failed to write edge to output stream", e);
+                               }
+                       }
+
+                       writer.writeEndElement();
+                       writer.writeEndDocument();
+                       writer.flush();
+               } catch (XMLStreamException e) {
+                       throw new RuntimeException(e);
+               }
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/ie/Importer.java b/src/main/java/org/openecomp/aai/champ/ie/Importer.java
new file mode 100644 (file)
index 0000000..c7aa6a1
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * ============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.openecomp.aai.champ.ie;
+
+import java.io.InputStream;
+
+import org.openecomp.aai.champ.ChampAPI;
+
+public interface Importer {
+
+       public void importData(ChampAPI api, InputStream is);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampCardinality.java b/src/main/java/org/openecomp/aai/champ/model/ChampCardinality.java
new file mode 100644 (file)
index 0000000..b9735ea
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============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.openecomp.aai.champ.model;
+
+public enum ChampCardinality {
+       SINGLE,
+       LIST,
+       SET
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampConnectionConstraint.java b/src/main/java/org/openecomp/aai/champ/model/ChampConnectionConstraint.java
new file mode 100644 (file)
index 0000000..c16e659
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * ============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.openecomp.aai.champ.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+@JsonDeserialize(builder = ChampConnectionConstraint.Builder.class)
+public final class ChampConnectionConstraint {
+
+       private final String sourceType;
+       private final String targetType;
+       private final ChampConnectionMultiplicity cardinality;
+
+       private ChampConnectionConstraint() {
+               throw new RuntimeException("Cannot call ConnectionConstraint() constructor");
+       }
+       
+       private ChampConnectionConstraint(Builder builder) {
+               this.sourceType = builder.sourceType;
+               this.targetType = builder.targetType;
+               this.cardinality = builder.multiplicity;
+       }
+       
+       public String getSourceType() { return sourceType; }
+       public String getTargetType() { return targetType; }
+       public ChampConnectionMultiplicity getMultiplicity() { return cardinality; }
+
+       @JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
+       public static class Builder {
+               private final String sourceType;
+               private final String targetType;
+
+               private ChampConnectionMultiplicity multiplicity = ChampConnectionMultiplicity.MANY;
+               
+               public Builder(@JsonProperty("sourceType") String sourceType, @JsonProperty("targetType") String targetType) {
+                       this.sourceType = sourceType;
+                       this.targetType = targetType;
+               }
+
+               public Builder multiplicity(ChampConnectionMultiplicity multiplicity) {
+                       this.multiplicity = multiplicity;
+                       return this;
+               }
+
+               public ChampConnectionConstraint build() {
+                       return new ChampConnectionConstraint(this);
+               }
+       }
+
+       @Override
+       public int hashCode() {
+               return 31 * (getSourceType().hashCode() + getTargetType().hashCode());
+       }
+
+       @Override
+       public boolean equals(Object o) {
+               if (o instanceof ChampConnectionConstraint) {
+                       final ChampConnectionConstraint connConstraint = (ChampConnectionConstraint) o;
+
+                       if (connConstraint.getSourceType().equals(getSourceType()) &&
+                               connConstraint.getTargetType().equals(getTargetType()) &&
+                               connConstraint.getMultiplicity().equals(getMultiplicity())) return true;
+               }
+
+               return false;
+       }
+
+       @Override
+       public String toString() {
+               return "{sourceType: " + getSourceType() +
+                               ", targetType: " + getTargetType() +
+                               ", multiplicity: " + getMultiplicity()
+                               + "}";
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampConnectionMultiplicity.java b/src/main/java/org/openecomp/aai/champ/model/ChampConnectionMultiplicity.java
new file mode 100644 (file)
index 0000000..337c03c
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============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.openecomp.aai.champ.model;
+
+public enum ChampConnectionMultiplicity {
+       NONE, //Cannot have any relationships of a type between two object types
+       ONE,  //Can have zero or one relationship of a type between two object types
+       MANY  //Can have zero or more relationships of a type between two object types
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampElement.java b/src/main/java/org/openecomp/aai/champ/model/ChampElement.java
new file mode 100644 (file)
index 0000000..329bbfd
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * ============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.openecomp.aai.champ.model;
+
+public interface ChampElement {
+
+       public boolean isObject();
+       public ChampObject asObject();
+
+       public boolean isRelationship();
+       public ChampRelationship asRelationship();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampField.java b/src/main/java/org/openecomp/aai/champ/model/ChampField.java
new file mode 100644 (file)
index 0000000..6d2df69
--- /dev/null
@@ -0,0 +1,129 @@
+/**
+ * ============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.openecomp.aai.champ.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+@JsonDeserialize(builder = ChampField.Builder.class)
+public final class ChampField implements Comparable<ChampField> {
+
+       private final String name;
+       private final ChampField.Type type;
+
+       private ChampField() {
+               throw new RuntimeException("Cannot use ChampField() constructor");
+       }
+       
+       public String getName() { return name; }
+       public ChampField.Type getType() { return type; }
+
+       @JsonIgnore
+       public Class<?> getJavaType() {
+               switch (type) {
+               case BOOLEAN:
+                       return Boolean.class;
+               case DOUBLE:
+                       return Double.class;
+               case FLOAT:
+                       return Float.class;
+               case INTEGER:
+                       return Integer.class;
+               case LONG:
+                       return Long.class;
+               case STRING:
+                       return String.class;
+               default:
+                       throw new RuntimeException("Unknown ChampField.Type " + type);
+               }
+       }
+
+       private ChampField(Builder builder) {
+               this.name = builder.name;
+               this.type = builder.type;
+       }
+
+       public static enum Type {
+               STRING,
+               INTEGER,
+               LONG,
+               DOUBLE,
+               FLOAT,
+               BOOLEAN
+       }
+
+       @JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
+       public static class Builder {
+               private final String name;
+
+               private ChampField.Type type = ChampField.Type.STRING;
+
+               public Builder(@JsonProperty("name") String name) {
+                       this.name = name;
+               }
+               
+               public Builder type(ChampField.Type type) {
+                       this.type = type;
+                       return this;
+               }
+
+               public ChampField build() {
+                       return new ChampField(this);
+               }
+       }
+
+       @Override
+       public int hashCode() {
+               return 31 * (getName().hashCode());
+       }
+
+       @Override
+       public boolean equals(Object object) {
+               if (object instanceof ChampField) {
+                       final ChampField champField = (ChampField) object;
+
+                       if (champField.getName().equals(getName())) return true;
+               }
+               
+               return false;
+       }
+
+       @Override
+       public String toString() {
+               return "{name: " + getName() +
+                               ", type: " + getType() +
+                               "}";
+       }
+
+       @Override
+       public int compareTo(ChampField o) {
+               final int nameComparison = getName().compareTo(o.getName());
+
+               if (nameComparison == 0) {
+                       return getType().compareTo(o.getType());
+               }
+
+               return nameComparison;
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampObject.java b/src/main/java/org/openecomp/aai/champ/model/ChampObject.java
new file mode 100644 (file)
index 0000000..f83d8ca
--- /dev/null
@@ -0,0 +1,235 @@
+/**
+ * ============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.openecomp.aai.champ.model;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+
+import org.openecomp.aai.champ.model.fluent.object.CreateChampObjectable;
+import org.openecomp.aai.champ.model.fluent.object.impl.CreateChampObjectableImpl;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public final class ChampObject implements ChampElement {
+
+       private final String type;
+       private final Optional<Object> key;
+       private final Map<String, Object> properties;
+
+       public static CreateChampObjectable create() {
+               return new CreateChampObjectableImpl();
+       }
+
+       private ChampObject() {
+               throw new RuntimeException("Attempted to call private AAIObject() constructor");
+       } //Not instantiable
+       
+       private ChampObject(Builder builder) {
+               this.type = builder.type;
+               this.key = builder.key;
+               this.properties = builder.properties;
+       }
+
+       @SuppressWarnings("unchecked")
+       public <T> Optional<T> getProperty(String key) {
+               if (!properties.containsKey(key)) return Optional.empty();
+
+               return Optional.of((T) properties.get(key));
+       }
+
+       public String getType() { return type; }
+       
+       @JsonIgnore
+       public Optional<Object> getKey() { return key; }
+       public Map<String, Object> getProperties() { return properties; }
+
+       @JsonProperty("key")
+       public Object getKeyValue() {
+         return key.orElse("");
+         
+       }
+       
+       public static class Builder {
+               private final String type;
+               private final Map<String, Object> properties = new HashMap<String, Object> ();
+               
+               private Optional<Object> key = Optional.empty();
+
+               public Builder(String type) {
+                       if (type == null) throw new IllegalArgumentException("Type cannot be null");
+
+                       this.type = type;
+               }
+
+               public Builder(ChampObject object) {
+                       type = object.getType();
+                       key = object.getKey();
+                       properties(object.getProperties());
+               }
+
+               public Builder key(Object key) {
+                       if (key == null) throw new IllegalArgumentException("Key cannot be set to null");
+
+                       this.key = Optional.of(key);
+                       return this;
+               }
+
+               public Builder property(String key, Object value) {
+                       if (key == null) throw new IllegalArgumentException("Property key cannot be null");
+                       if (value == null) throw new IllegalArgumentException("Property value cannot be null");
+
+                       if (ReservedPropertyKeys.contains(key)) throw new IllegalArgumentException("Property key " + key + " is reserved");
+
+                       properties.put(key, value);
+                       return this;
+               }
+
+               public Builder properties(Map<String, Object> properties) {
+                       for (Entry<String, Object> property : properties.entrySet()) {
+                               property(property.getKey(), property.getValue());
+                       }
+                       
+                       return this;
+               }
+               
+               public ChampObject build() {
+                       return new ChampObject(this);
+               }
+       }
+       
+       @Override
+       public boolean equals(Object object) {
+               if (this == object) return true;
+
+               if (object instanceof ChampObject) {
+                       final ChampObject champObj = (ChampObject) object;
+
+                       if (getKey().isPresent() && champObj.getKey().isPresent()) {
+
+                               if (getKey().get().equals(champObj.getKey().get())) return true;
+
+                       } else if (!getKey().isPresent() && !champObj.getKey().isPresent()) {
+                               if (getType().equals(champObj.getType()) &&
+                                       getProperties().equals(champObj.getProperties())) return true;
+                       }
+               }
+               
+               return false;
+       }
+
+       @Override
+       public int hashCode() {
+               if (getKey().isPresent()) return getKey().get().hashCode();
+
+               final int returnValue = 31 * (getType().hashCode() + getProperties().hashCode());
+               return returnValue;
+       }
+
+       @Override
+       public String toString() {
+               return "{key: " + (getKey().isPresent() ? getKey().get() : "")
+                       + ", type: " + getType()
+                       + ", properties: " + getProperties() + "}";
+       }
+       
+       public enum ReservedPropertyKeys {
+               CHAMP_OBJECT_TYPE ("aai_node_type"),
+               CHAMP_OBJECT_KEY ("key");
+
+               private final String text;
+
+               private ReservedPropertyKeys(final String text) {
+                       this.text = text;
+               } 
+               
+               public String toString() {
+                       return text;
+               }
+
+               public static boolean contains(String key) {
+                       for (ReservedPropertyKeys choice : ReservedPropertyKeys.values()) {
+                               if (choice.toString().equals(key)) return true;
+                       }
+
+                       return false;
+               }
+       }
+
+       public enum IgnoreOnReadPropertyKeys {
+               CHAMP_IMPORT_ASSIGNED_ID ("importAssignedId");
+               
+               private final String text;
+
+               private IgnoreOnReadPropertyKeys(final String text) {
+                       this.text = text;
+               } 
+               
+               public String toString() {
+                       return text;
+               }
+
+               public static boolean contains(String key) {
+                       for (IgnoreOnReadPropertyKeys choice : IgnoreOnReadPropertyKeys.values()) {
+                               if (choice.toString().equals(key)) return true;
+                       }
+
+                       return false;
+               }
+       }
+
+       public enum ReservedTypes {
+               ANY ("ANY");
+
+               private final String text;
+
+               private ReservedTypes(final String text) {
+                       this.text = text;
+               }
+
+               public String toString() {
+                       return text;
+               }
+       }
+
+       @Override
+       public boolean isObject() {
+               return true;
+       }
+
+       @Override
+       public ChampObject asObject() {
+               return this;
+       }
+
+       @Override
+       public boolean isRelationship() {
+               return false;
+       }
+
+       @Override
+       public ChampRelationship asRelationship() {
+               throw new UnsupportedOperationException("Cannot call asRelationship() on ChampObject");
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampObjectConstraint.java b/src/main/java/org/openecomp/aai/champ/model/ChampObjectConstraint.java
new file mode 100644 (file)
index 0000000..5afd803
--- /dev/null
@@ -0,0 +1,107 @@
+/**
+ * ============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.openecomp.aai.champ.model;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+@JsonDeserialize(builder = ChampObjectConstraint.Builder.class)
+public final class ChampObjectConstraint {
+
+       private final String type;
+       private final Map<String, ChampPropertyConstraint> propertyConstraints;
+       
+       private ChampObjectConstraint() {
+               throw new RuntimeException("Cannot call ChampObjectConstraint() constructor");
+       }
+       
+       private ChampObjectConstraint(Builder builder) {
+               this.type = builder.type;
+               this.propertyConstraints = builder.propertyConstraints;
+       }
+       
+       public String getType() { return type; }
+       public Set<ChampPropertyConstraint> getPropertyConstraints() { return new HashSet<ChampPropertyConstraint> (propertyConstraints.values()); }
+       
+       public Optional<ChampPropertyConstraint> getPropertyConstraint(String fieldName) {
+               if (!propertyConstraints.containsKey(fieldName)) return Optional.empty();
+
+               return Optional.of(propertyConstraints.get(fieldName));
+       }
+
+       @JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
+       public static class Builder {
+               private final String type;
+               private final Map<String, ChampPropertyConstraint> propertyConstraints;
+               
+               public Builder(@JsonProperty("type") String type) {
+                       this.type = type;
+                       this.propertyConstraints = new HashMap<String, ChampPropertyConstraint> ();
+               }
+
+               @JsonProperty("propertyConstraints")
+               public Builder constraints(Set<ChampPropertyConstraint> propertyConstraints) {
+
+                       for (ChampPropertyConstraint propConstraint : propertyConstraints) {
+                               constraint(propConstraint);
+                       }
+
+                       return this;
+               }
+
+               @JsonIgnore
+               public Builder constraint(ChampPropertyConstraint propConstraint) {
+                       propertyConstraints.put(propConstraint.getField().getName(), propConstraint);
+                       return this;
+               }
+
+               public ChampObjectConstraint build() {
+                       return new ChampObjectConstraint(this);
+               }
+       }
+
+       @Override
+       public boolean equals(Object o) {
+               if (o instanceof ChampObjectConstraint) {
+                       final ChampObjectConstraint objectConstraint = (ChampObjectConstraint) o;
+
+                       if (objectConstraint.getType().equals(getType())) return true;
+               }
+               
+               return false;
+       }
+
+       @Override
+       public String toString() {
+               return "{type: " + getType() +
+                               ", propertyConstraints: " + getPropertyConstraints() +
+                               "}";
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampObjectIndex.java b/src/main/java/org/openecomp/aai/champ/model/ChampObjectIndex.java
new file mode 100644 (file)
index 0000000..9b588c7
--- /dev/null
@@ -0,0 +1,87 @@
+/**
+ * ============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.openecomp.aai.champ.model;
+
+import org.openecomp.aai.champ.model.fluent.index.CreateObjectIndexable;
+import org.openecomp.aai.champ.model.fluent.index.impl.CreateObjectIndexableImpl;
+
+public final class ChampObjectIndex {
+
+       private final String name;
+       private final String type;
+       private final ChampField field;
+
+       public static CreateObjectIndexable create() {
+               return new CreateObjectIndexableImpl();
+       }
+
+       private ChampObjectIndex() {
+               throw new RuntimeException("Cannot call ChampObjectIndex() constructor");
+       }
+
+       private ChampObjectIndex(Builder builder) {
+               this.name = builder.name;
+               this.type = builder.type;
+               this.field = builder.field;
+       }
+
+       public String getName() { return name; }
+       public String getType() { return type; }
+       public ChampField getField() { return field; }
+
+       public static class Builder {
+               private final String name;
+               private final String type;
+               private final ChampField field;
+
+               public Builder(String name, String type, ChampField field) {
+                       this.name = name;
+                       this.type = type;
+                       this.field = field;
+               }
+
+               public ChampObjectIndex build() {
+                       return new ChampObjectIndex(this);
+               }
+       }
+
+       @Override
+       public String toString() {
+               return "{name: " + getName()
+                       + ", type: " + getType()
+                       + ", field: " + getField() + "}";
+       }
+
+       @Override
+       public boolean equals(Object object) {
+               if (this == object) return true;
+               
+               if (object instanceof ChampObjectIndex) {
+                       final ChampObjectIndex objectIndex = (ChampObjectIndex) object;
+                       
+                       if (getName().equals(objectIndex.getName()) &&
+                               getField().getName().equals(objectIndex.getField().getName())) return true;
+               }
+               
+               return false;
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampPartition.java b/src/main/java/org/openecomp/aai/champ/model/ChampPartition.java
new file mode 100644 (file)
index 0000000..ca7de74
--- /dev/null
@@ -0,0 +1,144 @@
+/**
+ * ============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.openecomp.aai.champ.model;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.openecomp.aai.champ.model.fluent.partition.CreateChampPartitionable;
+import org.openecomp.aai.champ.model.fluent.partition.impl.CreateChampPartionableImpl;
+
+public final class ChampPartition {
+
+       private final Set<ChampObject> champObjects;
+       private final Set<ChampRelationship> champRelationships;
+
+       private ChampPartition() {
+               throw new RuntimeException("Cannot call ChampGraph() constructor");
+       }
+       
+       private ChampPartition(Builder builder) {
+               this.champObjects = builder.champObjects;
+               this.champRelationships = builder.champRelationships;
+       }
+
+       public static CreateChampPartitionable create() {
+               return new CreateChampPartionableImpl();
+       }
+
+       public Set<ChampObject> getChampObjects() { return champObjects; }
+       public Set<ChampRelationship> getChampRelationships() { return champRelationships; }
+
+       public Set<ChampRelationship> getIncidentRelationships(ChampObject source) {
+               final Set<ChampRelationship> incidentRelationships = new HashSet<ChampRelationship> ();
+
+               for (ChampRelationship relationship : getChampRelationships()) {
+                       if (relationship.getSource().equals(source) ||
+                               relationship.getTarget().equals(source)) {
+                               incidentRelationships.add(relationship);
+                       }
+               }
+
+               return incidentRelationships;
+       }
+
+       public Map<String, Set<ChampRelationship>> getIncidentRelationshipsByType(ChampObject source) {
+               final Map<String, Set<ChampRelationship>> incidentRelationships = new HashMap<String, Set<ChampRelationship>> ();
+
+               for (ChampRelationship relationship : getChampRelationships()) {
+                       if (relationship.getSource().equals(source) ||
+                               relationship.getTarget().equals(source)) {
+                               if (!incidentRelationships.containsKey(relationship.getType())) {
+                                       incidentRelationships.put(relationship.getType(), new HashSet<ChampRelationship> ());
+                               }
+
+                               incidentRelationships.get(relationship.getType()).add(relationship);
+                       }
+               }
+
+               return incidentRelationships;
+       }
+
+       public static class Builder {
+               private final Set<ChampObject> champObjects;
+               private final Set<ChampRelationship> champRelationships;
+
+               public Builder() {
+                       this.champObjects = new HashSet<ChampObject> ();
+                       this.champRelationships = new HashSet<ChampRelationship> ();
+               }
+
+               public Builder object(ChampObject object) {
+                       champObjects.add(object);
+                       return this;
+               }
+
+               public Builder relationship(ChampRelationship relationship) {
+                       champRelationships.add(relationship);
+                       return this;
+               }
+
+               public Builder objects(Set<ChampObject> objects) {
+                       champObjects.addAll(objects);
+                       return this;
+               }
+               
+               public Builder relationships(Set<ChampRelationship> relationships) {
+                       champRelationships.addAll(relationships);
+                       return this;
+               }
+
+               public ChampPartition build() {
+                       return new ChampPartition(this);
+               }
+       }
+
+       @Override
+       public String toString() {
+
+               final StringBuilder sb = new StringBuilder();
+
+               sb.append("{objects: [");
+
+               for (ChampObject object : champObjects) {
+                       sb.append(object.toString());
+                       sb.append(",");
+               }
+
+               if (sb.charAt(sb.length() - 1) == ',') sb.deleteCharAt(sb.length() - 1); //Delete last comma
+
+               sb.append("], relationships: [");
+
+               for (ChampRelationship relationship : champRelationships) {
+                       sb.append(relationship.toString());
+                       sb.append(",");
+               }
+
+               if (sb.charAt(sb.length() - 1) == ',') sb.deleteCharAt(sb.length() - 1); //Delete last comma
+
+               sb.append("]}");
+
+               return sb.toString();
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampPropertyConstraint.java b/src/main/java/org/openecomp/aai/champ/model/ChampPropertyConstraint.java
new file mode 100644 (file)
index 0000000..d244f5a
--- /dev/null
@@ -0,0 +1,110 @@
+/**
+ * ============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.openecomp.aai.champ.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+@JsonDeserialize(builder = ChampPropertyConstraint.Builder.class)
+public final class ChampPropertyConstraint implements Comparable<ChampPropertyConstraint> {
+
+       private final ChampField field;
+       private final boolean required;
+       private final ChampCardinality cardinality;
+
+       private ChampPropertyConstraint() {
+               throw new RuntimeException("Cannot call ChampPropertyConstraint() constructor");
+       }
+       
+       private ChampPropertyConstraint(Builder builder) {
+               this.field = builder.field;
+               this.required = builder.required;
+               this.cardinality = builder.cardinality;
+       }
+
+       public ChampField getField() { return field; }
+       public boolean isRequired() { return required; }
+       public ChampCardinality getCardinality() { return cardinality; }
+
+       @JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
+       public static class Builder {
+               private final ChampField field;
+
+               private boolean required = false;
+               private ChampCardinality cardinality = ChampCardinality.SINGLE;
+
+               public Builder(@JsonProperty("field") ChampField field) {
+                       this.field = field;
+               }
+
+               public Builder required(boolean required) {
+                       this.required = required;
+                       return this;
+               }
+
+               public Builder cardinality(ChampCardinality cardinality) {
+                       this.cardinality = cardinality;
+                       return this;
+               }
+
+               public ChampPropertyConstraint build() {
+                       return new ChampPropertyConstraint(this);
+               }
+       }
+
+       @Override
+       public int hashCode() {
+               return 31 * (getField().hashCode());
+       }
+
+       @Override
+       public boolean equals(Object o) {
+               if (o instanceof ChampPropertyConstraint) {
+                       final ChampPropertyConstraint propertyConstraint = (ChampPropertyConstraint) o;
+
+                       if (propertyConstraint.getField().equals(getField()))
+                               return true;
+               }
+
+               return false;
+       }
+
+       @Override
+       public String toString() {
+               return "{field: " + getField() +
+                               ", required: " + isRequired() +
+                               ", cardinality: " + getCardinality() +
+                               "}";
+       }
+
+       @Override
+       public int compareTo(ChampPropertyConstraint o) {
+               final int fieldComparison = o.getField().compareTo(getField());
+
+               if (fieldComparison == 0) {
+                       return o.getCardinality().compareTo(getCardinality());
+               }
+
+               return fieldComparison;
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampRelationship.java b/src/main/java/org/openecomp/aai/champ/model/ChampRelationship.java
new file mode 100644 (file)
index 0000000..3e5e366
--- /dev/null
@@ -0,0 +1,217 @@
+/**
+ * ============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.openecomp.aai.champ.model;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+
+import org.openecomp.aai.champ.model.fluent.relationship.CreateChampRelationshipable;
+import org.openecomp.aai.champ.model.fluent.relationship.impl.CreateChampRelationshipableImpl;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public final class ChampRelationship implements ChampElement {
+       private final String type; //AKA edge label
+       private final Optional<Object> key;
+       private final Map<String, Object> properties;
+       private final ChampObject source;
+       private final ChampObject target;
+
+       public static CreateChampRelationshipable create() {
+               return new CreateChampRelationshipableImpl();
+       }
+
+       private ChampRelationship() { //Not instantiable
+               throw new RuntimeException("Cannot call ChampRelationship() constructor");
+       }
+       
+       private ChampRelationship(Builder builder) {
+               this.properties = builder.properties;
+               this.source = builder.source;
+               this.target = builder.target;
+               this.type = builder.type;
+               this.key = builder.key;
+       }
+
+       @JsonIgnore
+       public Optional<Object> getKey() {
+               return key;
+       }
+
+    @JsonProperty("key")
+    public Object getKeyValue() {
+      return key.orElse("");      
+    }
+          
+       public ChampObject getSource() {
+               return source;
+       }
+       
+       public ChampObject getTarget() {
+               return target;
+       }
+       
+       public String getType() {
+               return type;
+       }
+       
+       @SuppressWarnings("unchecked")
+       public <T> Optional<T> getProperty(String key) {
+               if (!properties.containsKey(key)) return Optional.empty();
+               
+               return Optional.of((T) properties.get(key));
+       }
+
+       public Map<String, Object> getProperties() {
+               return properties;
+       }
+
+       public static class Builder {
+               private final Map<String, Object> properties = new HashMap<String, Object> ();
+               private final ChampObject source;
+               private final ChampObject target;
+               private final String type;
+
+               private Optional<Object> key = Optional.empty();
+
+               public Builder(ChampObject source, ChampObject target, String type) {
+                       this.source = source;
+                       this.target = target;
+                       this.type = type;
+               }
+               
+               public Builder(ChampRelationship relationship) {
+                       this.source = relationship.source;
+                       this.target = relationship.target;
+                       this.type = relationship.type;
+
+                       properties.putAll(relationship.getProperties());
+               }
+
+               public Builder key(Object key) {
+                       this.key = Optional.of(key);
+                       return this;
+               }
+
+               public Builder properties(Map<String, Object> properties) {
+                       for (Entry<String, Object> property : properties.entrySet()) {
+                               property(property.getKey(), property.getValue());
+                       }
+
+                       return this;
+               }
+
+               public Builder property(String key, Object value) {
+                       if (ChampRelationship.ReservedPropertyKeys.contains(key)) throw new IllegalArgumentException("Cannot make use of reserved property key " + key);
+
+                       properties.put(key, value);
+                       return this;
+               }
+               
+               public ChampRelationship build() {
+                       return new ChampRelationship(this);
+               }
+       }
+
+       @Override
+       public boolean equals(Object object) {
+               if (this == object) return true;
+               if (object instanceof ChampRelationship) {
+                       final ChampRelationship champRelationship = (ChampRelationship) object;
+
+                       if (getKey().isPresent() && champRelationship.getKey().isPresent()) {
+                               if (getKey().get().equals(champRelationship.getKey().get())) return true;
+                       }
+               }
+               
+               return false;
+       }
+
+       @Override
+       public String toString() {
+               return "{key: " + (getKey().isPresent() ? getKey().get() : "") 
+                               + ", type: " + getType()
+                               + ", source: " + getSource()
+                               + ", target: " + getTarget()
+                               + ", properties: " + getProperties() + "}";
+       }
+
+       public enum ReservedPropertyKeys {
+               CHAMP_RELATIONSHIP_TYPE ("relationshipType"),
+               CHAMP_RELATIONSHIP_KEY ("key");
+
+               private final String text;
+
+               private ReservedPropertyKeys(final String text) {
+                       this.text = text;
+               } 
+               
+               public String toString() {
+                       return text;
+               }
+
+               public static boolean contains(String key) {
+                       for (ReservedPropertyKeys choice : ReservedPropertyKeys.values()) {
+                               if (choice.toString().equals(key)) return true;
+                       }
+
+                       return false;
+               }
+       }
+
+       public enum ReservedTypes {
+               ANY ("ANY");
+
+               private final String text;
+               
+               private ReservedTypes(final String text) {
+                       this.text = text;
+               }
+               
+               public String toString() {
+                       return text;
+               }
+       }
+
+       @Override
+       public boolean isObject() {
+               return false;
+       }
+
+       @Override
+       public ChampObject asObject() {
+               throw new UnsupportedOperationException("Cannot call asObject() on ChampRelationship");
+       }
+
+       @Override
+       public boolean isRelationship() {
+               return true;
+       }
+
+       @Override
+       public ChampRelationship asRelationship() {
+               return this;
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampRelationshipConstraint.java b/src/main/java/org/openecomp/aai/champ/model/ChampRelationshipConstraint.java
new file mode 100644 (file)
index 0000000..238637d
--- /dev/null
@@ -0,0 +1,139 @@
+/**
+ * ============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.openecomp.aai.champ.model;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+@JsonDeserialize(builder = ChampRelationshipConstraint.Builder.class)
+public final class ChampRelationshipConstraint {
+
+       private final String type;
+       private final Map<String, ChampPropertyConstraint> propertyConstraints;
+       private final Set<ChampConnectionConstraint> connectionConstraints;
+
+       private ChampRelationshipConstraint() {
+               throw new RuntimeException("Cannot call ChampObjectConstraint() constructor");
+       }
+       
+       private ChampRelationshipConstraint(Builder builder) {
+               this.type = builder.type;
+               this.propertyConstraints = builder.propertyConstraints;
+               this.connectionConstraints = builder.connectionConstraints;
+       }
+       
+       public String getType() { return type; }
+       public Set<ChampPropertyConstraint> getPropertyConstraints() { return new HashSet<ChampPropertyConstraint> (propertyConstraints.values()); }
+       
+       public Optional<ChampPropertyConstraint> getPropertyConstraint(String fieldName) {
+               if (!propertyConstraints.containsKey(fieldName)) return Optional.empty();
+
+               return Optional.of(propertyConstraints.get(fieldName));
+       }
+
+       public Set<ChampConnectionConstraint> getConnectionConstraints() { return connectionConstraints; }
+
+       public Optional<ChampConnectionConstraint> getConnectionConstraint(ChampRelationship incidentRelationship) {
+               if (!incidentRelationship.getType().equals(getType())) return Optional.empty();
+
+               final String sourceType = incidentRelationship.getSource().getType();
+               final String targetType = incidentRelationship.getTarget().getType();
+
+               for (ChampConnectionConstraint connConstraint : getConnectionConstraints()) {
+                       if (connConstraint.getSourceType().equals(sourceType) &&
+                               connConstraint.getTargetType().equals(targetType)) return Optional.of(connConstraint);
+               }
+
+               return Optional.empty();
+       }
+
+       @JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
+       public static class Builder {
+               private final String type;
+               private final Map<String, ChampPropertyConstraint> propertyConstraints;
+               private final Set<ChampConnectionConstraint> connectionConstraints;
+
+               public Builder(@JsonProperty("type") String type) {
+                       this.type = type;
+                       this.propertyConstraints = new HashMap<String, ChampPropertyConstraint> ();
+                       this.connectionConstraints = new HashSet<ChampConnectionConstraint> ();
+               }
+
+               public Builder propertyConstraints(Set<ChampPropertyConstraint> propertyConstraints) {
+
+                       for (ChampPropertyConstraint propConstraint : propertyConstraints) {
+                               constraint(propConstraint);
+                       }
+
+                       return this;
+               }
+
+               public Builder connectionConstraints(Set<ChampConnectionConstraint> connectionConstraints) {
+                       this.connectionConstraints.addAll(connectionConstraints);
+                       return this;
+               }
+
+               @JsonIgnore
+               public Builder constraint(ChampPropertyConstraint propConstraint) {
+                       propertyConstraints.put(propConstraint.getField().getName(), propConstraint);
+                       return this;
+               }
+
+               @JsonIgnore
+               public Builder constraint(ChampConnectionConstraint connConstraint) {
+                       connectionConstraints.add(connConstraint);
+                       return this;
+               }
+               public ChampRelationshipConstraint build() {
+                       return new ChampRelationshipConstraint(this);
+               }
+       }
+
+       @Override
+       public String toString() {
+               return "{type: " + getType() +
+                               ", connectionConstraints: " + getConnectionConstraints() +
+                               ", propertyConstraints: " + getPropertyConstraints() +
+                               "}";
+       }
+       @Override
+       public boolean equals(Object o) {
+               if (o instanceof ChampRelationshipConstraint) {
+                       final ChampRelationshipConstraint relConstraint = (ChampRelationshipConstraint) o;
+
+                       if (relConstraint.getType().equals(getType()) &&
+                               relConstraint.getConnectionConstraints().equals(getConnectionConstraints()) &&
+                               relConstraint.getPropertyConstraints().equals(getPropertyConstraints())) return true;
+               }
+               
+               return false;
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampRelationshipIndex.java b/src/main/java/org/openecomp/aai/champ/model/ChampRelationshipIndex.java
new file mode 100644 (file)
index 0000000..7f8f36f
--- /dev/null
@@ -0,0 +1,87 @@
+/**
+ * ============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.openecomp.aai.champ.model;
+
+import org.openecomp.aai.champ.model.fluent.index.CreateRelationshipIndexable;
+import org.openecomp.aai.champ.model.fluent.index.impl.CreateRelationshipIndexableImpl;
+
+public final class ChampRelationshipIndex {
+
+       private final String name;
+       private final String type;
+       private final ChampField field;
+
+       public static CreateRelationshipIndexable create() {
+               return new CreateRelationshipIndexableImpl();
+       }
+
+       private ChampRelationshipIndex() {
+               throw new RuntimeException("Cannot call ChampRelationshipIndex() constructor");
+       }
+
+       private ChampRelationshipIndex(Builder builder) {
+               this.name = builder.name;
+               this.type = builder.type;
+               this.field = builder.field;
+       }
+
+       public String getName() { return name; }
+       public String getType() { return type; }
+       public ChampField getField() { return field; }
+
+       public static class Builder {
+               private final String name;
+               private final String type;
+               private final ChampField field;
+
+               public Builder(String name, String type, ChampField field) {
+                       this.name = name;
+                       this.type = type;
+                       this.field = field;
+               }
+
+               public ChampRelationshipIndex build() {
+                       return new ChampRelationshipIndex(this);
+               }
+       }
+
+       @Override
+       public String toString() {
+               return "{name: " + getName()
+                       + ", type: " + getType()
+                       + ", field: " + getField() + "}";
+       }
+
+       @Override
+       public boolean equals(Object object) {
+               if (this == object) return true;
+               
+               if (object instanceof ChampRelationshipIndex) {
+                       final ChampRelationshipIndex relationshipIndex = (ChampRelationshipIndex) object;
+                       
+                       if (getName().equals(relationshipIndex.getName()) &&
+                               getField().getName().equals(relationshipIndex.getField().getName())) return true;
+               }
+               
+               return false;
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampSchema.java b/src/main/java/org/openecomp/aai/champ/model/ChampSchema.java
new file mode 100644 (file)
index 0000000..90c42b3
--- /dev/null
@@ -0,0 +1,148 @@
+/**
+ * ============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.openecomp.aai.champ.model;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+
+import org.openecomp.aai.champ.model.fluent.schema.CreateChampSchemable;
+import org.openecomp.aai.champ.model.fluent.schema.impl.CreateChampSchemableImpl;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+@JsonDeserialize(builder = ChampSchema.Builder.class)
+public final class ChampSchema {
+
+       private final Map<String, ChampObjectConstraint> objectConstraints;
+       private final Map<String, ChampRelationshipConstraint> relationshipConstraints;
+
+       private ChampSchema() {
+               throw new RuntimeException("Cannot call ChampSchema() constructor");
+       }
+       
+       private ChampSchema(Builder builder) {
+               this.objectConstraints = builder.objectConstraints;
+               this.relationshipConstraints = builder.relationshipConstraints;
+       }
+
+       public static CreateChampSchemable create() {
+               return new CreateChampSchemableImpl();
+       }
+
+       public Optional<ChampObjectConstraint> getObjectConstraint(String type) {
+               if (!getObjectConstraints().containsKey(type)) return Optional.empty();
+               
+               return Optional.of(getObjectConstraints().get(type));
+       }
+       
+       public Optional<ChampRelationshipConstraint> getRelationshipConstraint(String type) {
+               if (!getRelationshipConstraints().containsKey(type)) return Optional.empty();
+               
+               return Optional.of(getRelationshipConstraints().get(type));
+       }
+
+       public Map<String, ChampObjectConstraint> getObjectConstraints() {
+               return objectConstraints;
+       }
+
+       public Map<String, ChampRelationshipConstraint> getRelationshipConstraints() {
+               return relationshipConstraints;
+       }
+
+       @JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
+       public static class Builder {
+               private Map<String, ChampObjectConstraint> objectConstraints = new HashMap<String, ChampObjectConstraint> ();
+               private Map<String, ChampRelationshipConstraint> relationshipConstraints = new HashMap<String, ChampRelationshipConstraint> ();
+       
+               public Builder() {}
+
+               public Builder(ChampSchema schema) {
+                       objectConstraints.putAll(schema.getObjectConstraints());
+                       relationshipConstraints.putAll(schema.getRelationshipConstraints());
+               }
+
+               public Builder objectConstraints(Map<String, ChampObjectConstraint> objectConstraints) {
+                       this.objectConstraints.putAll(objectConstraints);
+                       return this;
+               }
+
+               public Builder relationshipConstraints(Map<String, ChampRelationshipConstraint> relationshipConstraints) {
+                       this.relationshipConstraints.putAll(relationshipConstraints);
+                       return this;
+               }
+
+               @JsonIgnore
+               public Builder constraint(ChampObjectConstraint objConstraint) {
+                       objectConstraints.put(objConstraint.getType(), objConstraint);
+                       return this;
+               }
+
+               @JsonIgnore
+               public Builder constraint(ChampRelationshipConstraint relConstraint) {
+                       relationshipConstraints.put(relConstraint.getType(), relConstraint);
+                       return this;
+               }
+
+               public ChampSchema build() {
+                       return new ChampSchema(this);
+               }
+       }
+
+       @Override
+       public boolean equals(Object schema) {
+               if (schema instanceof ChampSchema) {
+
+                       for (Entry<String, ChampObjectConstraint> objConstraint : getObjectConstraints().entrySet()) {
+                               final Optional<ChampObjectConstraint> champObjConstraint = ((ChampSchema) schema).getObjectConstraint(objConstraint.getKey());
+                               
+                               if (!champObjConstraint.isPresent() ||
+                                       !champObjConstraint.get().equals(objConstraint.getValue())) return false;
+                       }
+                       
+                       for (Entry<String, ChampRelationshipConstraint> relConstraint : getRelationshipConstraints().entrySet()) {
+                               final Optional<ChampRelationshipConstraint> champRelConstraint = ((ChampSchema) schema).getRelationshipConstraint(relConstraint.getKey());
+                               
+                               if (!champRelConstraint.isPresent() ||
+                                       !champRelConstraint.get().equals(relConstraint.getValue())) return false;
+                       }
+                       
+                       return true;
+               }
+               
+               return false;
+       }
+
+       @Override
+       public String toString() {
+               return "{objectConstraints: " + getObjectConstraints() +
+                               ", relationshipConstraints: " + getRelationshipConstraints() +
+                               "}";
+       }
+
+       public static ChampSchema emptySchema() {
+               return new ChampSchema.Builder().build();
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/BuildStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/BuildStep.java
new file mode 100644 (file)
index 0000000..4da29c3
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent;
+
+public interface BuildStep<T> {
+       public T build();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/KeyStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/KeyStep.java
new file mode 100644 (file)
index 0000000..291f087
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent;
+
+public interface KeyStep<T> {
+       public T withKey(Object key);
+       public T withoutKey();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/PropertiesStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/PropertiesStep.java
new file mode 100644 (file)
index 0000000..3f67650
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent;
+
+import java.util.Map;
+
+public interface PropertiesStep<T> {
+
+       public T withProperty(String key, Object value);
+       public T withProperties(Map<String, Object> properties);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/index/CreateObjectIndexable.java b/src/main/java/org/openecomp/aai/champ/model/fluent/index/CreateObjectIndexable.java
new file mode 100644 (file)
index 0000000..f2281ed
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.index;
+
+public interface CreateObjectIndexable {
+
+       public ObjectIndexTypeStep ofName(String name);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/index/CreateRelationshipIndexable.java b/src/main/java/org/openecomp/aai/champ/model/fluent/index/CreateRelationshipIndexable.java
new file mode 100644 (file)
index 0000000..e4f42ae
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.index;
+
+public interface CreateRelationshipIndexable {
+
+       public RelationshipIndexTypeStep ofName(String name);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/index/ObjectIndexFieldStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/index/ObjectIndexFieldStep.java
new file mode 100644 (file)
index 0000000..be1d60b
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.index;
+
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface ObjectIndexFieldStep {
+
+       public BuildStep<ChampObjectIndex> forField(String fieldName);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/index/ObjectIndexTypeStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/index/ObjectIndexTypeStep.java
new file mode 100644 (file)
index 0000000..99977e6
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.index;
+
+public interface ObjectIndexTypeStep {
+
+       public ObjectIndexFieldStep onType(String objectType);
+       public ObjectIndexFieldStep onAnyType();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/index/RelationshipIndexFieldStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/index/RelationshipIndexFieldStep.java
new file mode 100644 (file)
index 0000000..fb784e5
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.index;
+
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface RelationshipIndexFieldStep {
+
+       public BuildStep<ChampRelationshipIndex> forField(String fieldName);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/index/RelationshipIndexTypeStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/index/RelationshipIndexTypeStep.java
new file mode 100644 (file)
index 0000000..988dc05
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.index;
+
+public interface RelationshipIndexTypeStep {
+
+       public RelationshipIndexFieldStep onType(String objectType);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/index/impl/CreateObjectIndexableImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/index/impl/CreateObjectIndexableImpl.java
new file mode 100644 (file)
index 0000000..675dbb2
--- /dev/null
@@ -0,0 +1,77 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.index.impl;
+
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+import org.openecomp.aai.champ.model.fluent.index.CreateObjectIndexable;
+import org.openecomp.aai.champ.model.fluent.index.ObjectIndexFieldStep;
+import org.openecomp.aai.champ.model.fluent.index.ObjectIndexTypeStep;
+
+public final class CreateObjectIndexableImpl implements CreateObjectIndexable {
+
+       @Override
+       public ObjectIndexTypeStep ofName(String name) {
+               return new ObjectIndexTypeStep() {
+
+                       @Override
+                       public ObjectIndexFieldStep onType(String objectType) {
+                               return new ObjectIndexFieldStep() {
+
+                                       @Override
+                                       public BuildStep<ChampObjectIndex> forField(String fieldName) {
+                                               return new BuildStep<ChampObjectIndex> () {
+
+                                                       @Override
+                                                       public ChampObjectIndex build() {
+                                                               return new ChampObjectIndex.Builder(
+                                                                       name, objectType, new ChampField.Builder(fieldName).build()
+                                                               ).build();
+                                                       }
+                                               };
+                                       }
+                               };
+                       }
+
+                       @Override
+                       public ObjectIndexFieldStep onAnyType() {
+                               return new ObjectIndexFieldStep() {
+
+                                       @Override
+                                       public BuildStep<ChampObjectIndex> forField(String fieldName) {
+                                               return new BuildStep<ChampObjectIndex> () {
+
+                                                       @Override
+                                                       public ChampObjectIndex build() {
+                                                               return new ChampObjectIndex.Builder(
+                                                                       name, ChampObject.ReservedTypes.ANY.toString(), new ChampField.Builder(fieldName).build()
+                                                               ).build();
+                                                       }
+                                               };
+                                       }
+                               };
+                       }
+               };
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/index/impl/CreateRelationshipIndexableImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/index/impl/CreateRelationshipIndexableImpl.java
new file mode 100644 (file)
index 0000000..d41839e
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.index.impl;
+
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+import org.openecomp.aai.champ.model.fluent.index.CreateRelationshipIndexable;
+import org.openecomp.aai.champ.model.fluent.index.RelationshipIndexFieldStep;
+import org.openecomp.aai.champ.model.fluent.index.RelationshipIndexTypeStep;
+
+public final class CreateRelationshipIndexableImpl implements CreateRelationshipIndexable {
+
+       @Override
+       public RelationshipIndexTypeStep ofName(String name) {
+               return new RelationshipIndexTypeStep() {
+
+                       @Override
+                       public RelationshipIndexFieldStep onType(String relationshipType) {
+                               return new RelationshipIndexFieldStep() {
+
+                                       @Override
+                                       public BuildStep<ChampRelationshipIndex> forField(String fieldName) {
+                                               return new BuildStep<ChampRelationshipIndex> () {
+
+                                                       @Override
+                                                       public ChampRelationshipIndex build() {
+                                                               return new ChampRelationshipIndex.Builder(
+                                                                       name, relationshipType, new ChampField.Builder(fieldName).build()
+                                                               ).build();
+                                                       }
+                                               };
+                                       }
+                               };
+                       }
+               };
+       }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/object/CreateChampObjectable.java b/src/main/java/org/openecomp/aai/champ/model/fluent/object/CreateChampObjectable.java
new file mode 100644 (file)
index 0000000..79c2026
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.object;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.fluent.KeyStep;
+
+public interface CreateChampObjectable {
+       public KeyStep<ObjectBuildOrPropertiesStep> ofType(String type);
+       public KeyStep<ObjectBuildOrPropertiesStep> from(ChampObject object);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectBuildOrPropertiesStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectBuildOrPropertiesStep.java
new file mode 100644 (file)
index 0000000..73cdb78
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.object;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+import org.openecomp.aai.champ.model.fluent.PropertiesStep;
+
+public interface ObjectBuildOrPropertiesStep extends BuildStep<ChampObject>, PropertiesStep<ObjectBuildOrPropertiesStep> {
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectBuildOrPropertiesStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectBuildOrPropertiesStepImpl.java
new file mode 100644 (file)
index 0000000..5feabcf
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.object;
+
+import java.util.Map;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObject.Builder;
+
+public final class ObjectBuildOrPropertiesStepImpl implements ObjectBuildOrPropertiesStep {
+
+       private final ChampObject.Builder builder;
+
+       public ObjectBuildOrPropertiesStepImpl(Builder builder) {
+               this.builder = builder;
+       }
+
+       @Override
+       public ChampObject build() {
+               return builder.build();
+       }
+       
+       @Override
+       public ObjectBuildOrPropertiesStep withProperty(String key, Object value) {
+               builder.property(key, value);
+               return this;
+       }
+
+       @Override
+       public ObjectBuildOrPropertiesStep withProperties(Map<String, Object> properties) {
+               builder.properties(properties);
+               return this;
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectKeyStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectKeyStepImpl.java
new file mode 100644 (file)
index 0000000..a21aecc
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.object;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObject.Builder;
+import org.openecomp.aai.champ.model.fluent.KeyStep;
+
+public final class ObjectKeyStepImpl implements KeyStep<ObjectBuildOrPropertiesStep> {
+
+       private final ChampObject.Builder builder;
+
+       public ObjectKeyStepImpl(Builder builder) {
+               this.builder = builder;
+       }
+
+       @Override
+       public ObjectBuildOrPropertiesStep withKey(Object key) {
+               builder.key(key);
+               return new ObjectBuildOrPropertiesStepImpl(builder);
+       }
+
+       @Override
+       public ObjectBuildOrPropertiesStep withoutKey() {
+               return new ObjectBuildOrPropertiesStepImpl(builder);
+       }
+       
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/object/impl/CreateChampObjectableImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/object/impl/CreateChampObjectableImpl.java
new file mode 100644 (file)
index 0000000..4645b44
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.object.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.fluent.KeyStep;
+import org.openecomp.aai.champ.model.fluent.object.CreateChampObjectable;
+import org.openecomp.aai.champ.model.fluent.object.ObjectBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.object.ObjectKeyStepImpl;
+
+public final class CreateChampObjectableImpl implements CreateChampObjectable {
+
+       @Override
+       public KeyStep<ObjectBuildOrPropertiesStep> ofType(String type) {
+               return new ObjectKeyStepImpl(new ChampObject.Builder(type));
+       }
+
+       @Override
+       public KeyStep<ObjectBuildOrPropertiesStep> from(ChampObject object) {
+               return new ObjectKeyStepImpl(new ChampObject.Builder(object));
+       }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/partition/CreateChampPartitionable.java b/src/main/java/org/openecomp/aai/champ/model/fluent/partition/CreateChampPartitionable.java
new file mode 100644 (file)
index 0000000..e2a36fb
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.partition;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface CreateChampPartitionable extends BuildStep<ChampPartition> {
+
+       public CreateChampPartitionable withObject(ChampObject object);
+       public CreateChampPartitionable withRelationship(ChampRelationship relationship);
+       public ChampPartition build();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/partition/impl/CreateChampPartionableImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/partition/impl/CreateChampPartionableImpl.java
new file mode 100644 (file)
index 0000000..ddf9663
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.partition.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.partition.CreateChampPartitionable;
+
+public final class CreateChampPartionableImpl implements CreateChampPartitionable {
+
+       private final ChampPartition.Builder builder;
+
+       public CreateChampPartionableImpl() {
+               this.builder = new ChampPartition.Builder();
+       }
+
+       @Override
+       public CreateChampPartitionable withObject(ChampObject object) {
+               builder.object(object);
+               return this;
+       }
+
+       @Override
+       public CreateChampPartitionable withRelationship(ChampRelationship relationship) {
+               builder.relationship(relationship);
+               return this;
+       }
+
+       @Override
+       public ChampPartition build() {
+               return builder.build();
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/CreateChampRelationshipable.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/CreateChampRelationshipable.java
new file mode 100644 (file)
index 0000000..15b96f1
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship;
+
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.KeyStep;
+
+public interface CreateChampRelationshipable {
+       public KeyStep<SourceStep> ofType(String type);
+       public KeyStep<RelationshipBuildOrPropertiesStep> from(ChampRelationship relationship);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/RelationshipBuildOrPropertiesStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/RelationshipBuildOrPropertiesStep.java
new file mode 100644 (file)
index 0000000..09c18b8
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship;
+
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+import org.openecomp.aai.champ.model.fluent.PropertiesStep;
+
+public interface RelationshipBuildOrPropertiesStep extends BuildStep<ChampRelationship>, PropertiesStep<RelationshipBuildOrPropertiesStep> {
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceBuildOrPropertiesStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceBuildOrPropertiesStep.java
new file mode 100644 (file)
index 0000000..51d4dda
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship;
+
+public interface SourceBuildOrPropertiesStep {
+
+       public SourceBuildOrPropertiesStep withProperty(String key, Object value);
+       public TargetStep build();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceFromStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceFromStep.java
new file mode 100644 (file)
index 0000000..4dbc845
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship;
+
+import org.openecomp.aai.champ.model.ChampObject;
+
+public interface SourceFromStep {
+
+       public SourceBuildOrPropertiesStep from(ChampObject object);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceKeyStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceKeyStep.java
new file mode 100644 (file)
index 0000000..c054364
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship;
+
+public interface SourceKeyStep {
+
+       public SourceBuildOrPropertiesStep withKey(Object key);
+       public SourceBuildOrPropertiesStep withoutKey();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceStep.java
new file mode 100644 (file)
index 0000000..6e467bc
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship;
+
+public interface SourceStep {
+       public SourceTypeOrFromStep withSource();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceTypeOrFromStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceTypeOrFromStep.java
new file mode 100644 (file)
index 0000000..5597496
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship;
+
+public interface SourceTypeOrFromStep extends SourceTypeStep, SourceFromStep {
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceTypeStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceTypeStep.java
new file mode 100644 (file)
index 0000000..6c1f976
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship;
+
+public interface SourceTypeStep {
+
+       public SourceKeyStep ofType(String type);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetBuildOrPropertiesStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetBuildOrPropertiesStep.java
new file mode 100644 (file)
index 0000000..b02b7af
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship;
+
+public interface TargetBuildOrPropertiesStep {
+
+       public TargetBuildOrPropertiesStep withProperty(String key, Object value);
+       public RelationshipBuildOrPropertiesStep build();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetFromStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetFromStep.java
new file mode 100644 (file)
index 0000000..afcdf4a
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship;
+
+import org.openecomp.aai.champ.model.ChampObject;
+
+public interface TargetFromStep {
+
+       public TargetBuildOrPropertiesStep from(ChampObject object);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetKeyStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetKeyStep.java
new file mode 100644 (file)
index 0000000..e236be0
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship;
+
+public interface TargetKeyStep {
+
+       public TargetBuildOrPropertiesStep withKey(Object key);
+       public TargetBuildOrPropertiesStep withoutKey();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetStep.java
new file mode 100644 (file)
index 0000000..4a9f047
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship;
+
+public interface TargetStep {
+       public TargetTypeOrFromStep withTarget();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetTypeOrFromStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetTypeOrFromStep.java
new file mode 100644 (file)
index 0000000..31a5a63
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship;
+
+public interface TargetTypeOrFromStep extends TargetTypeStep, TargetFromStep {
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetTypeStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetTypeStep.java
new file mode 100644 (file)
index 0000000..308c06f
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship;
+
+public interface TargetTypeStep {
+
+       public TargetKeyStep ofType(String type);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/ChampRelationshipKeyStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/ChampRelationshipKeyStepImpl.java
new file mode 100644 (file)
index 0000000..2875d8a
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.KeyStep;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceStep;
+
+public final class ChampRelationshipKeyStepImpl implements KeyStep<SourceStep> {
+
+       private final String type;
+       private final ChampRelationship relationship;
+
+       public ChampRelationshipKeyStepImpl(String type) {
+               this.type = type;
+               this.relationship = null;
+       }
+
+       public ChampRelationshipKeyStepImpl(ChampRelationship relationship) {
+               this.type = null;
+               this.relationship = relationship;
+       }
+
+       @Override
+       public SourceStep withKey(Object key) {
+               return new SourceStepImpl(type, relationship, key);
+       }
+
+       @Override
+       public SourceStep withoutKey() {
+               return new SourceStepImpl(type, relationship, null);
+       }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/CreateChampRelationshipableImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/CreateChampRelationshipableImpl.java
new file mode 100644 (file)
index 0000000..3182416
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.KeyStep;
+import org.openecomp.aai.champ.model.fluent.relationship.CreateChampRelationshipable;
+import org.openecomp.aai.champ.model.fluent.relationship.RelationshipBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceStep;
+
+public final class CreateChampRelationshipableImpl implements CreateChampRelationshipable {
+
+       @Override
+       public KeyStep<SourceStep> ofType(String type) {
+               return new ChampRelationshipKeyStepImpl(type);
+       }
+
+       @Override
+       public KeyStep<RelationshipBuildOrPropertiesStep> from(ChampRelationship relationship) {
+               return new KeyStep<RelationshipBuildOrPropertiesStep> () {
+
+                       @Override
+                       public RelationshipBuildOrPropertiesStep withKey(Object key) {
+                               return new RelationshipBuildOrPropertiesStepImpl(new ChampRelationship.Builder(relationship).key(key));
+                       }
+
+                       @Override
+                       public RelationshipBuildOrPropertiesStep withoutKey() {
+                               return new RelationshipBuildOrPropertiesStepImpl(new ChampRelationship.Builder(relationship));
+                       }
+               };
+       }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/RelationshipBuildOrPropertiesStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/RelationshipBuildOrPropertiesStepImpl.java
new file mode 100644 (file)
index 0000000..6233b8f
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import java.util.Map;
+
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.RelationshipBuildOrPropertiesStep;
+
+public final class RelationshipBuildOrPropertiesStepImpl implements RelationshipBuildOrPropertiesStep {
+
+       private final ChampRelationship.Builder relationshipBuilder;
+       
+       public RelationshipBuildOrPropertiesStepImpl(ChampRelationship.Builder relationshipBuilder) {
+               this.relationshipBuilder = relationshipBuilder;
+       }
+
+       @Override
+       public ChampRelationship build() {
+               return relationshipBuilder.build();
+       }
+
+       @Override
+       public RelationshipBuildOrPropertiesStep withProperty(String key, Object value) {
+               relationshipBuilder.property(key, value);
+               return this;
+       }
+
+       @Override
+       public RelationshipBuildOrPropertiesStep withProperties(Map<String, Object> properties) {
+               relationshipBuilder.properties(properties);
+               return this;
+       }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceBuildOrPropertiesStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceBuildOrPropertiesStepImpl.java
new file mode 100644 (file)
index 0000000..e7324a3
--- /dev/null
@@ -0,0 +1,55 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetStep;
+
+public final class SourceBuildOrPropertiesStepImpl implements SourceBuildOrPropertiesStep {
+
+       private ChampObject.Builder sourceBuilder;
+       private Object relationshipKey;
+       private ChampRelationship relationship;
+       private String relationshipType;
+
+       public SourceBuildOrPropertiesStepImpl(String relationshipType, ChampRelationship relationship,
+                       Object relationshipKey, ChampObject.Builder sourceBuilder) {
+               this.relationshipType = relationshipType;
+               this.relationship = relationship;
+               this.relationshipKey = relationshipKey;
+               this.sourceBuilder = sourceBuilder;
+       }
+
+       @Override
+       public SourceBuildOrPropertiesStep withProperty(String key, Object value) {
+               sourceBuilder.property(key, value);
+               return this;
+       }
+       
+       @Override
+       public TargetStep build() {
+               return new TargetStepImpl(relationshipType, relationship, relationshipKey, sourceBuilder.build());
+       }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceKeyStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceKeyStepImpl.java
new file mode 100644 (file)
index 0000000..5b99d90
--- /dev/null
@@ -0,0 +1,56 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObject.Builder;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceKeyStep;
+
+public final class SourceKeyStepImpl implements SourceKeyStep {
+
+       private final String relationshipType;
+       private final ChampRelationship relationship;
+       private final Object relationshipKey;
+       private final ChampObject.Builder sourceBuilder;
+
+       public SourceKeyStepImpl(String relationshipType, ChampRelationship relationship, Object relationshipKey,
+                       Builder sourceBuilder) {
+               this.relationshipType = relationshipType;
+               this.relationship = relationship;
+               this.relationshipKey = relationshipKey;
+               this.sourceBuilder = sourceBuilder;
+       }
+
+       @Override
+       public SourceBuildOrPropertiesStep withKey(Object key) {
+               sourceBuilder.key(key);
+               return new SourceBuildOrPropertiesStepImpl(relationshipType, relationship, relationshipKey, sourceBuilder);
+       }
+       
+       @Override
+       public SourceBuildOrPropertiesStep withoutKey() {
+               return new SourceBuildOrPropertiesStepImpl(relationshipType, relationship, relationshipKey, sourceBuilder);
+       }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceStepImpl.java
new file mode 100644 (file)
index 0000000..9203072
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceStep;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceTypeOrFromStep;
+
+public final class SourceStepImpl implements SourceStep {
+
+       private final String relationshipType;
+       private final Object key;
+       private final ChampRelationship relationship;
+
+       public SourceStepImpl(String relationshipType, ChampRelationship relationship, Object key) {
+               this.relationshipType = relationshipType;
+               this.key = key;
+               this.relationship = relationship;
+       }
+
+       @Override
+       public SourceTypeOrFromStep withSource() {
+               return new SourceTypeOrFromStepImpl(relationshipType, relationship, key);
+       }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceTypeOrFromStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceTypeOrFromStepImpl.java
new file mode 100644 (file)
index 0000000..be7fa3d
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceKeyStep;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceTypeOrFromStep;
+
+public final class SourceTypeOrFromStepImpl implements SourceTypeOrFromStep {
+
+       private final String relationshipType;
+       private final Object relationshipKey;
+       private final ChampRelationship relationship;
+
+       public SourceTypeOrFromStepImpl(String relationshipType, ChampRelationship relationship, Object key) {
+               this.relationshipType = relationshipType;
+               this.relationship = relationship;
+               this.relationshipKey = key;
+       }
+
+       @Override
+       public SourceKeyStep ofType(String type) {
+               final ChampObject.Builder sourceBuilder = new ChampObject.Builder(type);
+               return new SourceKeyStepImpl(relationshipType, relationship, relationshipKey, sourceBuilder);
+       }
+       
+       @Override
+       public SourceBuildOrPropertiesStep from(ChampObject object) {
+               final ChampObject.Builder sourceBuilder = new ChampObject.Builder(object);
+               return new SourceBuildOrPropertiesStepImpl(relationshipType, relationship, relationshipKey, sourceBuilder);
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetBuildOrPropertiesStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetBuildOrPropertiesStepImpl.java
new file mode 100644 (file)
index 0000000..50ad6ab
--- /dev/null
@@ -0,0 +1,65 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.RelationshipBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetBuildOrPropertiesStep;
+
+public final class TargetBuildOrPropertiesStepImpl implements TargetBuildOrPropertiesStep {
+
+       private final String relationshipType;
+       private final ChampRelationship relationship;
+       private final Object relationshipKey;
+       private final ChampObject source;
+       private final ChampObject.Builder targetBuilder;
+
+       public TargetBuildOrPropertiesStepImpl(String relationshipType, ChampRelationship relationship, Object relationshipKey,
+                       ChampObject source, ChampObject.Builder targetBuilder) {
+               this.relationshipType = relationshipType;
+               this.relationship = relationship;
+               this.relationshipKey = relationshipKey;
+               this.source = source;
+               this.targetBuilder = targetBuilder;
+       }
+
+       @Override
+       public TargetBuildOrPropertiesStep withProperty(String key, Object value) {
+               targetBuilder.property(key, value);
+               return this;
+       }
+       
+       @Override
+       public RelationshipBuildOrPropertiesStep build() {
+
+               final ChampRelationship.Builder relationshipBuilder;
+
+               if (relationship != null) relationshipBuilder = new ChampRelationship.Builder(relationship);
+               else relationshipBuilder = new ChampRelationship.Builder(source, targetBuilder.build(), relationshipType);
+
+               if (relationshipKey != null) relationshipBuilder.key(relationshipKey);
+               
+               return new RelationshipBuildOrPropertiesStepImpl(relationshipBuilder);
+       }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetKeyStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetKeyStepImpl.java
new file mode 100644 (file)
index 0000000..0d4e45c
--- /dev/null
@@ -0,0 +1,56 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetKeyStep;
+
+public final class TargetKeyStepImpl implements TargetKeyStep {
+
+       private final String relationshipType;
+       private final Object relationshipKey;
+       private final ChampRelationship relationship;
+       private final ChampObject source;
+       private final ChampObject.Builder targetBuilder;
+
+       public TargetKeyStepImpl(String relationshipType, ChampRelationship relationship, Object key, ChampObject source, ChampObject.Builder targetBuilder) {
+               this.relationshipType = relationshipType;
+               this.relationship = relationship;
+               this.relationshipKey = key;
+               this.source = source;
+               this.targetBuilder = targetBuilder;
+       }
+
+       @Override
+       public TargetBuildOrPropertiesStep withKey(Object key) {
+               targetBuilder.key(key);
+               return new TargetBuildOrPropertiesStepImpl(relationshipType, relationship, relationshipKey, source, targetBuilder);
+       }
+       
+       @Override
+       public TargetBuildOrPropertiesStep withoutKey() {
+               return new TargetBuildOrPropertiesStepImpl(relationshipType, relationship, relationshipKey, source, targetBuilder);
+       }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetStepImpl.java
new file mode 100644 (file)
index 0000000..a0df564
--- /dev/null
@@ -0,0 +1,49 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetStep;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetTypeOrFromStep;
+
+public final class TargetStepImpl implements TargetStep {
+
+       private String relationshipType;
+       private ChampRelationship relationship;
+       private Object relationshipKey;
+       private ChampObject source;
+
+       public TargetStepImpl(String relationshipType, ChampRelationship relationship, Object relationshipKey,
+                       ChampObject source) {
+               this.relationshipType = relationshipType;
+               this.relationship = relationship;
+               this.relationshipKey = relationshipKey;
+               this.source = source;
+       }
+
+       @Override
+       public TargetTypeOrFromStep withTarget() {
+               return new TargetTypeOrFromStepImpl(relationshipType, relationship, relationshipKey, source);
+       }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetTypeOrFromStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetTypeOrFromStepImpl.java
new file mode 100644 (file)
index 0000000..e2f4153
--- /dev/null
@@ -0,0 +1,55 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetKeyStep;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetTypeOrFromStep;
+
+public final class TargetTypeOrFromStepImpl implements TargetTypeOrFromStep {
+
+       private final String relationshipType;
+       private final Object relationshipKey;
+       private final ChampRelationship relationship;
+       private final ChampObject source;
+
+       public TargetTypeOrFromStepImpl(String relationshipType, ChampRelationship relationship, Object key, ChampObject source) {
+               this.relationshipType = relationshipType;
+               this.relationship = relationship;
+               this.relationshipKey = key;
+               this.source = source;
+       }
+
+       @Override
+       public TargetKeyStep ofType(String type) {
+               final ChampObject.Builder targetBuilder = new ChampObject.Builder(type);
+               return new TargetKeyStepImpl(relationshipType, relationship, relationshipKey, source, targetBuilder);
+       }
+       
+       @Override
+       public TargetBuildOrPropertiesStep from(ChampObject object) {
+               final ChampObject.Builder targetBuilder = new ChampObject.Builder(object);
+               return new TargetBuildOrPropertiesStepImpl(relationshipType, relationship, relationshipKey, source, targetBuilder);
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/CreateChampSchemable.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/CreateChampSchemable.java
new file mode 100644 (file)
index 0000000..804f8c0
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface CreateChampSchemable extends BuildStep<ChampSchema> {
+
+       public ObjectConstraintTypeStep withObjectConstraint();
+       public RelationshipConstraintTypeStep withRelationshipConstraint();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintBuildStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintBuildStep.java
new file mode 100644 (file)
index 0000000..00ea332
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface ObjectConstraintBuildStep extends BuildStep<ObjectConstraintPropertyStep> {
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintFieldStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintFieldStep.java
new file mode 100644 (file)
index 0000000..ea8d00c
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+public interface ObjectConstraintFieldStep {
+
+       public ObjectConstraintSubStep onField(String name);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintFieldTypeStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintFieldTypeStep.java
new file mode 100644 (file)
index 0000000..fe88b34
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.ChampField;
+
+public interface ObjectConstraintFieldTypeStep {
+
+       public ObjectConstraintRequiredOptionalStep ofType(ChampField.Type type);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintPropertyStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintPropertyStep.java
new file mode 100644 (file)
index 0000000..921af34
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface ObjectConstraintPropertyStep extends BuildStep<CreateChampSchemable> {
+
+       public ObjectConstraintFieldStep withPropertyConstraint();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintRequiredOptionalStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintRequiredOptionalStep.java
new file mode 100644 (file)
index 0000000..5d40c24
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.ChampCardinality;
+
+public interface ObjectConstraintRequiredOptionalStep {
+
+       public ObjectConstraintBuildStep required();
+       public ObjectConstraintBuildStep optional();
+       public ObjectConstraintRequiredOptionalStep cardinality(ChampCardinality cardinality);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintSubStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintSubStep.java
new file mode 100644 (file)
index 0000000..709cea6
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+public interface ObjectConstraintSubStep extends ObjectConstraintFieldTypeStep, ObjectConstraintRequiredOptionalStep{
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintTypeStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintTypeStep.java
new file mode 100644 (file)
index 0000000..a81c30f
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+public interface ObjectConstraintTypeStep {
+
+       public ObjectConstraintPropertyStep onType(String type);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintBuildStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintBuildStep.java
new file mode 100644 (file)
index 0000000..9fb3e30
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface RelationshipConstraintBuildStep extends BuildStep<RelationshipConstraintSubStep> {
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintFieldStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintFieldStep.java
new file mode 100644 (file)
index 0000000..2ef66b4
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+public interface RelationshipConstraintFieldStep {
+
+       public RelationshipConstraintPropertyOptionalsStep onField(String name);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintFieldTypeStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintFieldTypeStep.java
new file mode 100644 (file)
index 0000000..67d612c
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.ChampField;
+
+public interface RelationshipConstraintFieldTypeStep {
+
+       public RelationshipConstraintRequiredOptionalStep ofType(ChampField.Type type);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintMultiplicityStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintMultiplicityStep.java
new file mode 100644 (file)
index 0000000..2c712de
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.ChampConnectionMultiplicity;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface RelationshipConstraintMultiplicityStep extends BuildStep<RelationshipConstraintSubStep> {
+
+       public BuildStep<RelationshipConstraintSubStep> withMultiplicity(ChampConnectionMultiplicity multiplicity);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintPropertyOptionalsStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintPropertyOptionalsStep.java
new file mode 100644 (file)
index 0000000..f8ea388
--- /dev/null
@@ -0,0 +1,25 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+public interface RelationshipConstraintPropertyOptionalsStep extends RelationshipConstraintFieldTypeStep, RelationshipConstraintRequiredOptionalStep {
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintRequiredOptionalStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintRequiredOptionalStep.java
new file mode 100644 (file)
index 0000000..b318d73
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+public interface RelationshipConstraintRequiredOptionalStep {
+
+       public RelationshipConstraintBuildStep required();
+       public RelationshipConstraintBuildStep optional();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintSourceStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintSourceStep.java
new file mode 100644 (file)
index 0000000..a497f56
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+public interface RelationshipConstraintSourceStep {
+
+       public RelationshipConstraintTargetStep sourcedFrom(String sourceType);
+       public RelationshipConstraintTargetStep sourcedFromAny();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintSubStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintSubStep.java
new file mode 100644 (file)
index 0000000..67fda0e
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface RelationshipConstraintSubStep extends BuildStep<CreateChampSchemable> {
+
+       public RelationshipConstraintFieldStep withPropertyConstraint();
+       public RelationshipConstraintSourceStep withConnectionConstraint();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintTargetStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintTargetStep.java
new file mode 100644 (file)
index 0000000..0b31918
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+public interface RelationshipConstraintTargetStep {
+
+       public RelationshipConstraintMultiplicityStep targetedTo(String targetType);
+       public RelationshipConstraintMultiplicityStep targetedToAny();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintTypeStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintTypeStep.java
new file mode 100644 (file)
index 0000000..e0f7d7b
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema;
+
+public interface RelationshipConstraintTypeStep {
+
+       public RelationshipConstraintSubStep onType(String type);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/CreateChampSchemableImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/CreateChampSchemableImpl.java
new file mode 100644 (file)
index 0000000..2d1c2ed
--- /dev/null
@@ -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.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.schema.CreateChampSchemable;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintPropertyStep;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintTypeStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintSubStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintTypeStep;
+
+public class CreateChampSchemableImpl implements CreateChampSchemable {
+
+       private final ChampSchema.Builder schemaBuilder;
+       
+       public CreateChampSchemableImpl() {
+               this.schemaBuilder = new ChampSchema.Builder();
+       }
+
+       public CreateChampSchemableImpl(ChampSchema.Builder schemaBuilder) {
+               this.schemaBuilder = schemaBuilder;
+       }
+
+       @Override
+       public ChampSchema build() {
+               return schemaBuilder.build();
+       }
+
+       @Override
+       public ObjectConstraintTypeStep withObjectConstraint() {
+               return new ObjectConstraintTypeStep() {
+
+                       @Override
+                       public ObjectConstraintPropertyStep onType(String type) {
+                               return new ObjectConstraintPropertyStepImpl(schemaBuilder, 
+                                                                                                                       new ChampObjectConstraint.Builder(type));
+                       }
+               };
+       }
+
+       @Override
+       public RelationshipConstraintTypeStep withRelationshipConstraint() {
+               return new RelationshipConstraintTypeStep() {
+
+                       @Override
+                       public RelationshipConstraintSubStep onType(String type) {
+                               return new RelationshipConstraintSubStepImpl(schemaBuilder,
+                                                                                                                        new ChampRelationshipConstraint.Builder(type));
+                       }
+               };
+       }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintBuildStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintBuildStepImpl.java
new file mode 100644 (file)
index 0000000..d344350
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintBuildStep;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintPropertyStep;
+
+public class ObjectConstraintBuildStepImpl implements ObjectConstraintBuildStep {
+
+       private final ChampSchema.Builder schemaBuilder;
+       private final ChampObjectConstraint.Builder constraintBuilder;
+
+       public ObjectConstraintBuildStepImpl(ChampSchema.Builder schemaBuilder, ChampObjectConstraint.Builder constraintBuilder) {
+               this.schemaBuilder = schemaBuilder;
+               this.constraintBuilder = constraintBuilder;
+       }
+
+       @Override
+       public ObjectConstraintPropertyStep build() {
+               return new ObjectConstraintPropertyStepImpl(schemaBuilder, constraintBuilder);
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintFieldStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintFieldStepImpl.java
new file mode 100644 (file)
index 0000000..9ce1d03
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintFieldStep;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintSubStep;
+
+public class ObjectConstraintFieldStepImpl implements ObjectConstraintFieldStep {
+
+       private final ChampSchema.Builder schemaBuilder;
+       private final ChampObjectConstraint.Builder constraintBuilder;
+
+       public ObjectConstraintFieldStepImpl(ChampSchema.Builder schemaBuilder, ChampObjectConstraint.Builder constraintBuilder) {
+               this.schemaBuilder = schemaBuilder;
+               this.constraintBuilder = constraintBuilder;
+       }
+
+       @Override
+       public ObjectConstraintSubStep onField(String name) {
+               return new ObjectConstraintSubStepImpl(schemaBuilder, constraintBuilder, new ChampField.Builder(name));
+       }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintPropertyStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintPropertyStepImpl.java
new file mode 100644 (file)
index 0000000..de2406e
--- /dev/null
@@ -0,0 +1,50 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.schema.CreateChampSchemable;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintFieldStep;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintPropertyStep;
+
+public class ObjectConstraintPropertyStepImpl implements ObjectConstraintPropertyStep {
+
+       private final ChampObjectConstraint.Builder constraintBuilder;
+       private final ChampSchema.Builder schemaBuilder;
+
+       public ObjectConstraintPropertyStepImpl(ChampSchema.Builder schemaBuilder, ChampObjectConstraint.Builder constraintBuilder) {
+               this.constraintBuilder = constraintBuilder;
+               this.schemaBuilder = schemaBuilder;
+       }
+
+       @Override
+       public CreateChampSchemable build() {
+               return new CreateChampSchemableImpl(schemaBuilder.constraint(constraintBuilder.build()));
+       }
+       
+       @Override
+       public ObjectConstraintFieldStep withPropertyConstraint() {
+               return new ObjectConstraintFieldStepImpl(schemaBuilder, constraintBuilder);
+       }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintSubStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintSubStepImpl.java
new file mode 100644 (file)
index 0000000..01eba74
--- /dev/null
@@ -0,0 +1,111 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampCardinality;
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampPropertyConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintBuildStep;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintPropertyStep;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintRequiredOptionalStep;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintSubStep;
+
+public class ObjectConstraintSubStepImpl implements ObjectConstraintSubStep {
+
+       private final ChampSchema.Builder schemaBuilder;
+       private final ChampObjectConstraint.Builder constraintBuilder;
+       private final ChampField.Builder fieldBuilder;
+       private final ChampPropertyConstraint.Builder propConstBuilder;
+
+       public ObjectConstraintSubStepImpl(ChampSchema.Builder schemaBuilder,
+                       ChampObjectConstraint.Builder constraintBuilder,
+                       ChampField.Builder fieldBuilder) {
+               this.schemaBuilder = schemaBuilder;
+               this.constraintBuilder = constraintBuilder;
+               this.fieldBuilder = fieldBuilder;
+               this.propConstBuilder = new ChampPropertyConstraint.Builder(fieldBuilder.build());
+
+       }
+
+       @Override
+       public ObjectConstraintRequiredOptionalStep ofType(ChampField.Type type) {
+               return new ObjectConstraintRequiredOptionalStep() {
+                       final ChampPropertyConstraint.Builder propConstBuilder = new ChampPropertyConstraint.Builder(fieldBuilder.type(type).build());
+
+                       @Override
+                       public ObjectConstraintBuildStep required() {
+                               constraintBuilder.constraint(propConstBuilder.required(true).build());
+
+                               return new ObjectConstraintBuildStep() {
+
+                                       @Override
+                                       public ObjectConstraintPropertyStep build() {
+                                               return new ObjectConstraintPropertyStepImpl(schemaBuilder, constraintBuilder);
+                                       }
+                               };
+                       }
+
+                       @Override
+                       public ObjectConstraintBuildStep optional() {
+                               constraintBuilder.constraint(propConstBuilder.required(false).build());
+
+                               return new ObjectConstraintBuildStep() {
+
+                                       @Override
+                                       public ObjectConstraintPropertyStep build() {
+                                               return new ObjectConstraintPropertyStepImpl(schemaBuilder, constraintBuilder);
+                                       }
+                               };
+                       }
+
+                       @Override
+                       public ObjectConstraintRequiredOptionalStep cardinality(ChampCardinality cardinality) {
+                               propConstBuilder.cardinality(cardinality);
+                               return this;
+                       }
+               };
+       }
+       
+       @Override
+       public ObjectConstraintBuildStep required() {
+               constraintBuilder.constraint(propConstBuilder.required(true).build());
+
+               return new ObjectConstraintBuildStepImpl(schemaBuilder, constraintBuilder);
+       }
+       
+       @Override
+       public ObjectConstraintBuildStep optional() {
+               final ChampPropertyConstraint.Builder propConstBuilder = new ChampPropertyConstraint.Builder(fieldBuilder.build());
+
+               constraintBuilder.constraint(propConstBuilder.required(false).build());
+
+               return new ObjectConstraintBuildStepImpl(schemaBuilder, constraintBuilder);
+       }
+
+       @Override
+       public ObjectConstraintRequiredOptionalStep cardinality(ChampCardinality cardinality) {
+               propConstBuilder.cardinality(cardinality);
+               return this;
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintBuildStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintBuildStepImpl.java
new file mode 100644 (file)
index 0000000..018c73f
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.ChampSchema.Builder;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintBuildStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintSubStep;
+
+public class RelationshipConstraintBuildStepImpl implements RelationshipConstraintBuildStep {
+
+       private final ChampSchema.Builder schemaBuilder;
+       private final ChampRelationshipConstraint.Builder relConstraintBuilder;
+
+       public RelationshipConstraintBuildStepImpl(Builder schemaBuilder,
+                       ChampRelationshipConstraint.Builder relConstraintBuilder) {
+               this.schemaBuilder = schemaBuilder;
+               this.relConstraintBuilder = relConstraintBuilder;
+       }
+
+       @Override
+       public RelationshipConstraintSubStep build() {
+               return new RelationshipConstraintSubStepImpl(schemaBuilder, relConstraintBuilder);
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintPropertyOptionalsStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintPropertyOptionalsStepImpl.java
new file mode 100644 (file)
index 0000000..62176b7
--- /dev/null
@@ -0,0 +1,68 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampPropertyConstraint;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintBuildStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintPropertyOptionalsStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintRequiredOptionalStep;
+
+public class RelationshipConstraintPropertyOptionalsStepImpl implements RelationshipConstraintPropertyOptionalsStep {
+
+       private final ChampSchema.Builder schemaBuilder;
+       private final ChampRelationshipConstraint.Builder relConstraintBuilder;
+       private final ChampField.Builder fieldBuilder;
+
+       public RelationshipConstraintPropertyOptionalsStepImpl(ChampSchema.Builder schemaBuilder,
+                       ChampRelationshipConstraint.Builder relConstraintBuilder,
+                       ChampField.Builder fieldBuilder) {
+               this.schemaBuilder = schemaBuilder;
+               this.relConstraintBuilder = relConstraintBuilder;
+               this.fieldBuilder = fieldBuilder;
+       }
+
+       @Override
+       public RelationshipConstraintRequiredOptionalStep ofType(ChampField.Type type) {
+               return new RelationshipConstraintRequiredOptionalStepImpl(schemaBuilder, relConstraintBuilder, fieldBuilder.type(type));
+       }
+       
+       @Override
+       public RelationshipConstraintBuildStep required() {
+               final ChampPropertyConstraint propConstraint = new ChampPropertyConstraint.Builder(fieldBuilder.build())
+                                                                                                                                                                       .required(true)
+                                                                                                                                                                       .build();
+
+               return new RelationshipConstraintBuildStepImpl(schemaBuilder, relConstraintBuilder.constraint(propConstraint));
+       }
+       
+       @Override
+       public RelationshipConstraintBuildStep optional() {
+               final ChampPropertyConstraint propConstraint = new ChampPropertyConstraint.Builder(fieldBuilder.build())
+                                                                                                                                                                       .required(false)
+                                                                                                                                                                       .build();
+
+               return new RelationshipConstraintBuildStepImpl(schemaBuilder, relConstraintBuilder.constraint(propConstraint));
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintRequiredOptionalStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintRequiredOptionalStepImpl.java
new file mode 100644 (file)
index 0000000..63720a4
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampPropertyConstraint;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.ChampSchema.Builder;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintBuildStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintRequiredOptionalStep;
+
+public class RelationshipConstraintRequiredOptionalStepImpl implements RelationshipConstraintRequiredOptionalStep {
+
+       private final ChampSchema.Builder schemaBuilder;
+       private final ChampRelationshipConstraint.Builder relConstraintBuilder;
+       private final ChampField.Builder fieldBuilder;
+
+       public RelationshipConstraintRequiredOptionalStepImpl(Builder schemaBuilder,
+                                                                                                                       ChampRelationshipConstraint.Builder relConstraintBuilder,
+                                                                                                                       ChampField.Builder fieldBuilder) {
+               this.schemaBuilder = schemaBuilder;
+               this.relConstraintBuilder = relConstraintBuilder;
+               this.fieldBuilder = fieldBuilder;
+       }
+
+       @Override
+       public RelationshipConstraintBuildStep required() {
+               final ChampPropertyConstraint propConstraint = new ChampPropertyConstraint.Builder(fieldBuilder.build())
+                                                                                                                                                                       .required(true)
+                                                                                                                                                                       .build();
+
+               return new RelationshipConstraintBuildStepImpl(schemaBuilder, relConstraintBuilder.constraint(propConstraint));
+       }
+       
+       @Override
+       public RelationshipConstraintBuildStep optional() {
+               final ChampPropertyConstraint propConstraint = new ChampPropertyConstraint.Builder(fieldBuilder.build())
+                                                                                                                                                                       .required(false)
+                                                                                                                                                                       .build();
+
+               return new RelationshipConstraintBuildStepImpl(schemaBuilder, relConstraintBuilder.constraint(propConstraint));
+       }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintSubStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintSubStepImpl.java
new file mode 100644 (file)
index 0000000..4d88690
--- /dev/null
@@ -0,0 +1,205 @@
+/**
+ * ============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.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampConnectionConstraint;
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampConnectionMultiplicity;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+import org.openecomp.aai.champ.model.fluent.schema.CreateChampSchemable;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintFieldStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintMultiplicityStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintPropertyOptionalsStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintSourceStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintSubStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintTargetStep;
+
+public class RelationshipConstraintSubStepImpl implements RelationshipConstraintSubStep {
+
+       private final ChampSchema.Builder schemaBuilder;
+       private final ChampRelationshipConstraint.Builder relConstraintBuilder;
+
+       public RelationshipConstraintSubStepImpl(ChampSchema.Builder schemaBuilder,
+                               ChampRelationshipConstraint.Builder relConstraintBuilder) {
+               this.schemaBuilder = schemaBuilder;
+               this.relConstraintBuilder = relConstraintBuilder;
+       }
+       
+       @Override
+       public CreateChampSchemable build() {
+               return new CreateChampSchemableImpl(schemaBuilder.constraint(relConstraintBuilder.build()));
+       }
+
+       @Override
+       public RelationshipConstraintFieldStep withPropertyConstraint() {
+               return new RelationshipConstraintFieldStep() {
+
+                       @Override
+                       public RelationshipConstraintPropertyOptionalsStep onField(String name) {
+                               return new RelationshipConstraintPropertyOptionalsStepImpl(schemaBuilder, relConstraintBuilder, new ChampField.Builder(name));
+                       }
+               };
+       }
+       
+       @Override
+       public RelationshipConstraintSourceStep withConnectionConstraint() {
+               return new RelationshipConstraintSourceStep() {
+
+                       @Override
+                       public RelationshipConstraintTargetStep sourcedFrom(String sourceType) {
+                               
+                               return new RelationshipConstraintTargetStep() {
+                                       
+                                       @Override
+                                       public RelationshipConstraintMultiplicityStep targetedTo(String targetType) {
+                                               final ChampConnectionConstraint.Builder connectionConstraint = new ChampConnectionConstraint.Builder(sourceType, targetType);
+
+                                               return new RelationshipConstraintMultiplicityStep() {
+               
+                                                       @Override
+                                                       public RelationshipConstraintSubStep build() {
+                                                               relConstraintBuilder.constraint(connectionConstraint.build());
+                                                       
+                                                               return RelationshipConstraintSubStepImpl.this;
+                                                       }
+               
+                                                       @Override
+                                                       public BuildStep<RelationshipConstraintSubStep> withMultiplicity(
+                                                                       ChampConnectionMultiplicity multiplicity) {
+                                                               connectionConstraint.multiplicity(multiplicity);
+                                                               return new BuildStep<RelationshipConstraintSubStep> () {
+
+                                                                       @Override
+                                                                       public RelationshipConstraintSubStep build() {
+                                                                               relConstraintBuilder.constraint(connectionConstraint.build());
+
+                                                                               return RelationshipConstraintSubStepImpl.this;
+                                                                       }
+                                                               };
+                                                       }
+                                               };
+                                       }
+
+                                       @Override
+                                       public RelationshipConstraintMultiplicityStep targetedToAny() {
+                                               final ChampConnectionConstraint.Builder connectionConstraint = new ChampConnectionConstraint.Builder(sourceType, ChampObject.ReservedTypes.ANY.toString());
+
+                                               return new RelationshipConstraintMultiplicityStep() {
+               
+                                                       @Override
+                                                       public RelationshipConstraintSubStep build() {
+                                                               relConstraintBuilder.constraint(connectionConstraint.build());
+                                                       
+                                                               return RelationshipConstraintSubStepImpl.this;
+                                                       }
+               
+                                                       @Override
+                                                       public BuildStep<RelationshipConstraintSubStep> withMultiplicity(
+                                                                       ChampConnectionMultiplicity multiplicity) {
+                                                               connectionConstraint.multiplicity(multiplicity);
+                                                               return new BuildStep<RelationshipConstraintSubStep> () {
+
+                                                                       @Override
+                                                                       public RelationshipConstraintSubStep build() {
+                                                                               relConstraintBuilder.constraint(connectionConstraint.build());
+
+                                                                               return RelationshipConstraintSubStepImpl.this;
+                                                                       }
+                                                               };
+                                                       }
+                                               };
+                                       }
+                               };
+                       }
+
+                       @Override
+                       public RelationshipConstraintTargetStep sourcedFromAny() {
+                               return new RelationshipConstraintTargetStep() {
+                                       
+                                       @Override
+                                       public RelationshipConstraintMultiplicityStep targetedTo(String targetType) {
+                                               final ChampConnectionConstraint.Builder connectionConstraint = new ChampConnectionConstraint.Builder(ChampObject.ReservedTypes.ANY.toString(), targetType);
+
+                                               return new RelationshipConstraintMultiplicityStep() {
+               
+                                                       @Override
+                                                       public RelationshipConstraintSubStep build() {
+                                                               relConstraintBuilder.constraint(connectionConstraint.build());
+                                                       
+                                                               return RelationshipConstraintSubStepImpl.this;
+                                                       }
+               
+                                                       @Override
+                                                       public BuildStep<RelationshipConstraintSubStep> withMultiplicity(
+                                                                       ChampConnectionMultiplicity multiplicity) {
+                                                               connectionConstraint.multiplicity(multiplicity);
+                                                               return new BuildStep<RelationshipConstraintSubStep> () {
+
+                                                                       @Override
+                                                                       public RelationshipConstraintSubStep build() {
+                                                                               relConstraintBuilder.constraint(connectionConstraint.build());
+
+                                                                               return RelationshipConstraintSubStepImpl.this;
+                                                                       }
+                                                               };
+                                                       }
+                                               };
+                                       }
+
+                                       @Override
+                                       public RelationshipConstraintMultiplicityStep targetedToAny() {
+                                               final ChampConnectionConstraint.Builder connectionConstraint = new ChampConnectionConstraint.Builder(ChampObject.ReservedTypes.ANY.toString(), ChampObject.ReservedTypes.ANY.toString());
+
+                                               return new RelationshipConstraintMultiplicityStep() {
+               
+                                                       @Override
+                                                       public RelationshipConstraintSubStep build() {
+                                                               relConstraintBuilder.constraint(connectionConstraint.build());
+                                                       
+                                                               return RelationshipConstraintSubStepImpl.this;
+                                                       }
+               
+                                                       @Override
+                                                       public BuildStep<RelationshipConstraintSubStep> withMultiplicity(
+                                                                       ChampConnectionMultiplicity multiplicity) {
+                                                               connectionConstraint.multiplicity(multiplicity);
+                                                               return new BuildStep<RelationshipConstraintSubStep> () {
+
+                                                                       @Override
+                                                                       public RelationshipConstraintSubStep build() {
+                                                                               relConstraintBuilder.constraint(connectionConstraint.build());
+
+                                                                               return RelationshipConstraintSubStepImpl.this;
+                                                                       }
+                                                               };
+                                                       }
+                                               };
+                                       }
+                               };
+                       }
+               };
+       }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/perf/ChampAPIPerformanceTest.java b/src/main/java/org/openecomp/aai/champ/perf/ChampAPIPerformanceTest.java
new file mode 100644 (file)
index 0000000..563ae67
--- /dev/null
@@ -0,0 +1,430 @@
+/**
+ * ============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.openecomp.aai.champ.perf;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.graph.impl.InMemoryChampGraphImpl;
+import org.openecomp.aai.champ.graph.impl.TitanChampGraphImpl;
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.schema.ChampSchemaEnforcer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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;
+
+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("--champ.graph.type=")) {
+                       throw new RuntimeException("Must provide --champ.graph.type=" + ChampGraph.Type.values() + " as first parameter");
+               }
+
+               final ChampGraph.Type graphType = ChampGraph.Type.valueOf(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 == ChampGraph.Type.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("champ.schema.enforcer")) {
+                               final String schemaEnforcerClassStr = settings.get("champ.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);
+               }
+
+               if (graph.queryObjects(Collections.emptyMap()).limit(1).count() > 0) {
+                       graph.shutdown();
+                       throw new RuntimeException("Expected empty graph");
+               }
+
+               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();
+
+                       final Stream<ChampRelationship> objects = graph.queryRelationships(Collections.singletonMap("relationshipNumber", i));
+                       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();
+                       final Stream<ChampObject> objects = graph.queryObjects(Collections.singletonMap("objectNumber", i));
+                       
+                       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();
+               final Stream<ChampObject> objects = graph.queryObjects( 
+                                                       Collections.singletonMap(
+                                                               ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString(), "foo"
+                                                       )
+                                               );
+               
+               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();
+               final Stream<ChampRelationship> relationships = graph.queryRelationships(
+                                                       Collections.singletonMap(
+                                                               ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString(), "bazz"
+                                                       )
+                                               );
+                                               
+               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()
+                               );
+
+                               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
+                       }
+               }
+
+               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()
+                               );
+
+                               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);
+                       }
+               }
+
+               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/src/main/java/org/openecomp/aai/champ/schema/AlwaysValidChampSchemaEnforcer.java b/src/main/java/org/openecomp/aai/champ/schema/AlwaysValidChampSchemaEnforcer.java
new file mode 100644 (file)
index 0000000..25083b1
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * ============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.openecomp.aai.champ.schema;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+
+public final class AlwaysValidChampSchemaEnforcer implements ChampSchemaEnforcer {
+
+       @Override
+       public void validate(ChampObject champObject, ChampObjectConstraint champObjectConstraint) {
+       }
+
+       @Override
+       public void validate(ChampRelationship champRelationship,
+                       ChampRelationshipConstraint champRelationshipConstraint) {
+       }
+
+       @Override
+       public void validate(ChampPartition champPartition, ChampSchema schema) {
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/schema/ChampSchemaEnforcer.java b/src/main/java/org/openecomp/aai/champ/schema/ChampSchemaEnforcer.java
new file mode 100644 (file)
index 0000000..7918e14
--- /dev/null
@@ -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.openecomp.aai.champ.schema;
+
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+
+public interface ChampSchemaEnforcer {
+
+       public void validate(ChampObject champObject, ChampObjectConstraint champObjectConstraint) throws ChampSchemaViolationException;
+       public void validate(ChampRelationship champRelationship, ChampRelationshipConstraint champRelationshipConstraint) throws ChampSchemaViolationException;
+       public void validate(ChampPartition champPartition, ChampSchema schema) throws ChampSchemaViolationException;
+}
diff --git a/src/main/java/org/openecomp/aai/champ/schema/DefaultChampSchemaEnforcer.java b/src/main/java/org/openecomp/aai/champ/schema/DefaultChampSchemaEnforcer.java
new file mode 100644 (file)
index 0000000..19ae46f
--- /dev/null
@@ -0,0 +1,144 @@
+/**
+ * ============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.openecomp.aai.champ.schema;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.model.ChampConnectionConstraint;
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampPropertyConstraint;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+
+public final class DefaultChampSchemaEnforcer implements ChampSchemaEnforcer {
+
+       @Override
+       public void validate(ChampObject champObject, ChampObjectConstraint champObjectConstraint) throws ChampSchemaViolationException {
+               for (ChampPropertyConstraint pc : champObjectConstraint.getPropertyConstraints()) {
+                       final ChampField field = pc.getField();
+                       final Optional<Object> property = champObject.getProperty(field.getName());
+                       
+                       if (pc.isRequired() && !property.isPresent()) {
+                               throw new ChampSchemaViolationException("Required property " + pc.getField().getName() + " is not present");
+                       }
+
+                       if (property.isPresent()) {
+                               switch (pc.getCardinality()) {
+                               case SINGLE:
+                                       if (!pc.getField().getJavaType().isInstance(property.get())) {
+                                               throw new ChampSchemaViolationException("Expected type " + pc.getField().getType() + " for type " + pc.getField().getName());
+                                       }
+                                       break;
+                               case LIST:
+                                       if (!(property.get() instanceof List)) throw new ChampSchemaViolationException("Expected List type for ChampCardinality." + pc.getCardinality());
+                                       break;
+                               case SET:
+                                       if (!(property.get() instanceof Set)) throw new ChampSchemaViolationException("Expected Set type for ChampCardinality." + pc.getCardinality());
+                                       break;
+                               default:
+                                       throw new RuntimeException("Unknown property constraint cardinality " + pc.getCardinality());
+                               }
+                       }
+               }
+       }
+
+       @Override
+       public void validate(ChampRelationship champRelationship,
+                       ChampRelationshipConstraint champRelationshipConstraint) throws ChampSchemaViolationException {
+
+               for (ChampPropertyConstraint pc : champRelationshipConstraint.getPropertyConstraints()) {
+                       final ChampField field = pc.getField();
+                       final Optional<Object> property = champRelationship.getProperty(field.getName());
+                       
+                       if (pc.isRequired() && !property.isPresent()) {
+                               throw new ChampSchemaViolationException("Required property " + pc.getField().getName() + " is not present");
+                       }
+
+                       if (property.isPresent() && !pc.getField().getJavaType().isInstance(property.get())) {
+                               throw new ChampSchemaViolationException("Expected type " + pc.getField().getType() + " for type " + pc.getField().getName());
+                       }
+               }
+       }
+
+       @Override
+       public void validate(ChampPartition champPartition, ChampSchema schema) throws ChampSchemaViolationException {
+       
+               for (ChampObject object : champPartition.getChampObjects()) {
+                       final Optional<ChampObjectConstraint> objConstraint = schema.getObjectConstraint(object.getType());
+
+                       if (!objConstraint.isPresent()) continue;
+                       
+                       validate(object, objConstraint.get());
+
+                       final Map<String, Set<ChampRelationship>> incidentRelationshipsByType = champPartition.getIncidentRelationshipsByType(object);
+
+                       for (Map.Entry<String, Set<ChampRelationship>> incidentRelationshipsOfType : incidentRelationshipsByType.entrySet()) {
+                               final Optional<ChampRelationshipConstraint> relConstraint = schema.getRelationshipConstraint(incidentRelationshipsOfType.getKey());
+
+                               if (relConstraint.isPresent()) {
+                                       final ChampRelationshipConstraint relationshipConstraint = relConstraint.get();
+                                       final Map<ChampConnectionConstraint, AtomicInteger> connectionCounts = new HashMap<ChampConnectionConstraint, AtomicInteger> ();
+
+                                       for (ChampRelationship incidentRelationship : incidentRelationshipsOfType.getValue()) {
+                                               final Optional<ChampConnectionConstraint> connectionConstraint = relationshipConstraint.getConnectionConstraint(incidentRelationship);
+
+                                               validate(incidentRelationship, relationshipConstraint);
+
+                                               if (connectionConstraint.isPresent()) {
+
+                                                       if (!connectionCounts.containsKey(connectionConstraint.get())) {
+                                                               connectionCounts.put(connectionConstraint.get(), new AtomicInteger(0));
+                                                       }
+
+                                                       final int connectionCount = connectionCounts.get(connectionConstraint.get()).incrementAndGet();
+
+                                                       switch (connectionConstraint.get().getMultiplicity()) {
+                                                       case MANY:
+                                                               //Always valid
+                                                       break;
+                                                       case NONE:
+                                                               if (connectionCount > 0) throw new ChampSchemaViolationException("Violated connection constraint " + connectionConstraint.get());
+                                                       break;
+                                                       case ONE:
+                                                               if (connectionCount > 1) throw new ChampSchemaViolationException("Violated connection constraint " + connectionConstraint.get());
+                                                       break;
+                                                       default:
+                                                       break;
+                                                       }
+                                               }
+                                       }
+                               }
+                               
+                       }
+               }
+       }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/transform/Champformer.java b/src/main/java/org/openecomp/aai/champ/transform/Champformer.java
new file mode 100644 (file)
index 0000000..dc79cd8
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * ============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.openecomp.aai.champ.transform;
+
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+
+public interface Champformer<V, E> {
+       public V marshallObject(ChampObject object) throws ChampUnmarshallingException;
+       public E marshallRelationship(ChampRelationship relationship) throws ChampUnmarshallingException;
+
+       public ChampObject unmarshallObject(V data) throws ChampUnmarshallingException;
+       public ChampRelationship unmarshallRelationship(E data) throws ChampUnmarshallingException;
+}
diff --git a/src/main/java/org/openecomp/aai/champ/transform/TinkerpopChampformer.java b/src/main/java/org/openecomp/aai/champ/transform/TinkerpopChampformer.java
new file mode 100644 (file)
index 0000000..412c8c3
--- /dev/null
@@ -0,0 +1,94 @@
+/**
+ * ============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.openecomp.aai.champ.transform;
+
+import java.util.Iterator;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.object.ObjectBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.relationship.RelationshipBuildOrPropertiesStep;
+
+public final class TinkerpopChampformer implements Champformer<Vertex, Edge> {
+
+       @Override
+       public Vertex marshallObject(ChampObject object) throws ChampUnmarshallingException {
+               throw new UnsupportedOperationException("Cannot marshall object to Tinkerpop Vertex without adding it to a graph");
+       }
+
+       @Override
+       public Edge marshallRelationship(ChampRelationship relationship) throws ChampUnmarshallingException {
+               throw new UnsupportedOperationException("Cannot marshall relationships to Tinkerpop Edge without adding it to a graph");
+       }
+
+       @Override
+       public ChampObject unmarshallObject(Vertex vertex) throws ChampUnmarshallingException {
+               final String type = vertex.label();
+               final ObjectBuildOrPropertiesStep aaiObjBuilder = ChampObject.create()
+                                                                                                                       .ofType(type)
+                                                                                                                       .withKey(vertex.id());
+               final Iterator<VertexProperty<Object>> properties = vertex.properties();
+
+               while (properties.hasNext()) {
+                       final VertexProperty<Object> property = properties.next();
+
+                       if (ChampObject.ReservedPropertyKeys.contains(property.key()) ||
+                               ChampObject.IgnoreOnReadPropertyKeys.contains(property.key())) continue;
+                       
+                       aaiObjBuilder.withProperty(property.key(), property.value());
+               }
+
+               return aaiObjBuilder.build();
+       }
+
+       @Override
+       public ChampRelationship unmarshallRelationship(Edge edge) throws ChampUnmarshallingException {
+               final ChampObject source = unmarshallObject(edge.outVertex());
+               final ChampObject target = unmarshallObject(edge.inVertex());
+               final String type = edge.label();
+               final RelationshipBuildOrPropertiesStep aaiRelBuilder = ChampRelationship.create()
+                                                                                                                                                       .ofType(type)
+                                                                                                                                                       .withKey(edge.id())
+                                                                                                                                                       .withSource()
+                                                                                                                                                               .from(source)
+                                                                                                                                                               .build()
+                                                                                                                                                       .withTarget()
+                                                                                                                                                               .from(target)
+                                                                                                                                                               .build();
+               final Iterator<Property<Object>> properties = edge.properties();
+               
+               while (properties.hasNext()) {
+                       final Property<Object> property = properties.next();
+                       
+                       if (ChampRelationship.ReservedPropertyKeys.contains(property.key())) continue;
+
+                       aaiRelBuilder.withProperty(property.key(), property.value());
+               }
+               
+               return aaiRelBuilder.build();
+       }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/concurrency/ConcurrencyTest.java b/src/test/java/org/openecomp/aai/champ/concurrency/ConcurrencyTest.java
new file mode 100644 (file)
index 0000000..a9a6b75
--- /dev/null
@@ -0,0 +1,72 @@
+/**
+ * ============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.openecomp.aai.champ.concurrency;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.core.ChampObjectTest;
+import org.openecomp.aai.champ.core.ChampRelationshipTest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ConcurrencyTest {
+
+       private static final Logger LOGGER = LoggerFactory.getLogger(ConcurrencyTest.class);
+
+       @Test
+       public void runConcurrentTest() {
+               for (ChampGraph.Type apiType : ChampGraph.Type.values()) {
+                       final ChampAPI api = ChampAPI.Factory.newInstance(apiType);
+                       runConcurrencyTest(api);
+                       api.shutdown();
+               }
+       }
+
+       private void runConcurrencyTest(ChampAPI api) {
+               final int numThreads = 10;
+               final ExecutorService es = Executors.newFixedThreadPool(numThreads);
+
+               for (int i = 0; i < numThreads * 2; i++) {
+                       es.submit(new Runnable() {
+                               @Override
+                               public void run() {
+                                       final ChampGraph graph = api.getGraph(ConcurrencyTest.class.getSimpleName());
+                                       ChampObjectTest.testChampObjectCrud(graph);
+                                       ChampRelationshipTest.testChampRelationshipCrud(graph);
+                               }
+                       });
+               }
+
+               try {
+                       es.shutdown();
+                       es.awaitTermination(60, TimeUnit.SECONDS);
+               } catch (InterruptedException e) {
+                       LOGGER.warn("Interrupted while waiting for concurrency test to finish", e);
+                       return;
+               }
+       }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/BaseChampAPITest.java b/src/test/java/org/openecomp/aai/champ/core/BaseChampAPITest.java
new file mode 100644 (file)
index 0000000..7d374c3
--- /dev/null
@@ -0,0 +1,50 @@
+/**
+ * ============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.openecomp.aai.champ.core;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BaseChampAPITest {
+
+       private static final Logger LOGGER = LoggerFactory.getLogger(BaseChampAPITest.class);
+
+       protected static void cleanUp(String graphName) {
+               LOGGER.warn("cleanUp is disabled for unit tests - you can enable it by commenting out the code below this log message, please don't commit it though");
+
+               /*
+               LOGGER.debug("Cleaning up graph {}", graphName);
+
+               try {
+                       final TitanGraph graph = TitanFactory.build()
+                                                                               .set("storage.backend", "cassandra")
+                                                                               .set("storage.hostname", "localhost")
+                                                                               .set("storage.cassandra.keyspace", graphName)
+                                                                               .open();
+                       graph.close();
+                       TitanCleanup.clear(graph);
+               } catch (IllegalArgumentException e) {
+                       LOGGER.warn("Could not clean up graph - unable to instantiate");
+               }
+               */
+       }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampAPITest.java b/src/test/java/org/openecomp/aai/champ/core/ChampAPITest.java
new file mode 100644 (file)
index 0000000..7cd259e
--- /dev/null
@@ -0,0 +1,239 @@
+/**
+ * ============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.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+
+public class ChampAPITest {
+
+       @Test
+       public void testChampAPIInstantiation() {
+
+               for (ChampGraph.Type type : ChampGraph.Type.values()) {
+
+                       final ChampAPI api = ChampAPI.Factory.newInstance(type);
+
+                       assertTrue(type == ChampGraph.Type.valueOf(type.name()));
+                       assertTrue(api.getType() == type);
+       
+                       api.getGraph("foo");
+                       api.shutdown();
+       
+                       try {
+                               api.getGraph("foo");
+                               throw new AssertionError("Able to call getGraph(String name) after shutdown()");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+               }
+       }
+
+       @Test
+       public void testChampGraphInstantiation() throws Exception {
+               for (ChampGraph.Type type : ChampGraph.Type.values()) {
+                       final ChampGraph graph = ChampGraph.Factory.newInstance(type, "foo");
+
+                       graph.shutdown();
+
+                       try {
+                               graph.deleteObject(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.deleteObjectIndex(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.deletePartition(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.deleteRelationship(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.deleteRelationshipIndex(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.deleteSchema();
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.queryObjects(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.queryRelationships(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.retrieveObject(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.retrieveObjectIndex(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.retrieveObjectIndices();
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.retrieveRelationship(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.retrieveRelationshipIndex(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.retrieveRelationshipIndices();
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.retrieveRelationships(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.retrieveSchema();
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.storeObject(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.storeObjectIndex(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.storePartition(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.storeRelationship(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.storeRelationshipIndex(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.storeSchema(null);
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.updateSchema(new ChampObjectConstraint.Builder("").build());
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.updateSchema(new ChampRelationshipConstraint.Builder("").build());
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+
+                       try {
+                               graph.shutdown();
+                               throw new AssertionError("Able to call API method after shutdown was initiated");
+                       } catch (IllegalStateException e) {
+                               //Expected
+                       }
+               }
+       }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampElementTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampElementTest.java
new file mode 100644 (file)
index 0000000..4d34f8a
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * ============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.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.model.ChampElement;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+
+public class ChampElementTest {
+
+       @Test
+       public void testChampElement() {
+               final ChampElement fooElement = new ChampObject.Builder("foo").build();
+
+               assertTrue(fooElement.isObject());
+               assertTrue(!fooElement.isRelationship());
+               assertTrue(fooElement.asObject() instanceof ChampObject);
+
+               try {
+                       fooElement.asRelationship();
+                       throw new AssertionError("Failed to throw exception when calling asRelationship() on a ChampObject");
+               } catch (UnsupportedOperationException e) {
+                       //Expected
+               }
+
+               final ChampElement barElement = new ChampObject.Builder("bar").build();
+               final ChampElement usesElement = new ChampRelationship.Builder(fooElement.asObject(), barElement.asObject(), "uses").build();
+
+               assertTrue(usesElement.isRelationship());
+               assertTrue(!usesElement.isObject());
+               assertTrue(usesElement.asRelationship() instanceof ChampRelationship);
+
+               try {
+                       usesElement.asObject();
+                       throw new AssertionError("Failed to throw exception when calling asObject() on a ChampRelationship");
+               } catch (UnsupportedOperationException e) {
+                       //Expected
+               }
+       }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampFieldTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampFieldTest.java
new file mode 100644 (file)
index 0000000..f852d9d
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * ============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.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.model.ChampField;
+
+public class ChampFieldTest {
+
+       @Test
+       public void testChampField() {
+               final ChampField a = new ChampField.Builder("a")
+                                                                                       .type(ChampField.Type.STRING)
+                                                                                       .build();
+
+               final ChampField aEquivalent = new ChampField.Builder("a")
+                                                                                                               .build();
+
+               final ChampField b = new ChampField.Builder("b")
+                                                                                       .build();
+
+               assertTrue(a.equals(aEquivalent));
+               assertTrue(!a.equals(new Object()));
+               assertTrue(!a.equals(b));
+
+               assertTrue(a.compareTo(aEquivalent) == 0);
+               assertTrue(a.compareTo(b) != 0);
+
+               assertTrue(a.toString().equals(aEquivalent.toString()));
+               assertTrue(!a.toString().equals(b.toString()));
+       }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampObjectIndexTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampObjectIndexTest.java
new file mode 100644 (file)
index 0000000..6f71664
--- /dev/null
@@ -0,0 +1,142 @@
+/**
+ * ============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.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.exceptions.ChampIndexNotExistsException;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+
+public class ChampObjectIndexTest extends BaseChampAPITest {
+       @Test
+       public void runTest() {
+               for (ChampGraph.Type apiType : ChampGraph.Type.values()) {
+                       final ChampAPI api = ChampAPI.Factory.newInstance(apiType);
+                       final String graphName = api.getClass().getSimpleName();
+
+                       switch (apiType) {
+                       case IN_MEMORY:
+                       break;
+                       case TITAN:
+                               cleanUp(graphName);
+                       break;
+                       default:
+                       break;
+                       }
+
+                       ChampObjectIndexTest.testChampObjectIndexCrud(api.getGraph(graphName));
+
+                       api.shutdown();
+               }
+       }
+
+       private static void testChampObjectIndexCrud(ChampGraph graph) {
+               
+               final ChampObjectIndex objectIndex = ChampObjectIndex.create()
+                                                                                                                               .ofName("fooObjectIndex")
+                                                                                                                               .onType("foo")
+                                                                                                                               .forField("propertyName")
+                                                                                                                               .build();
+
+               testChampObjectIndexStorage(graph, objectIndex);
+               testChampObjectIndexDelete(graph, objectIndex);
+       }
+
+       private static void testChampObjectIndexDelete(ChampGraph graph, ChampObjectIndex objectIndex) {
+
+               if (!graph.capabilities().canDeleteObjectIndices()) {
+                       try {
+                               graph.deleteObjectIndex("someindex");
+                               throw new AssertionError("Graph claims it does not support object index delete, but failed to throw UnsupportedOperationException");
+                       } catch (UnsupportedOperationException e) {
+                       } catch (ChampIndexNotExistsException e) {
+                               throw new AssertionError("Graph claims it does not support object index delete, but failed to throw UnsupportedOperationException");
+                       }
+               } else {
+                       try {
+                               graph.deleteObjectIndex(objectIndex.getName());
+                               
+                               final Optional<ChampObjectIndex> retrievedObjectIndex = graph.retrieveObjectIndex(objectIndex.getName());
+       
+                               if (retrievedObjectIndex.isPresent()) throw new AssertionError("Retrieved object index after deleting it");
+       
+                               final Stream<ChampObjectIndex> retrievedObjectIndices = graph.retrieveObjectIndices();
+                               final Collection<ChampObjectIndex> allObjectIndices = retrievedObjectIndices.collect(Collectors.toList());
+       
+                               if (allObjectIndices.contains(objectIndex)) throw new AssertionError("Retrieve all indices contained index previously deleted");
+                               if (allObjectIndices.size() != 0) throw new AssertionError("Wrong number of indices returned by retrieve all indices");
+                       
+                       } catch (ChampIndexNotExistsException e) {
+                               throw new AssertionError(e);
+                       }
+       
+                       try {
+                               graph.deleteObjectIndex(objectIndex.getName());
+                               throw new AssertionError("Failed to throw exception on non-existent object index");
+                       } catch (ChampIndexNotExistsException e) {
+                               //Expected
+                       }
+               }
+       }
+
+       private static void testChampObjectIndexStorage(ChampGraph graph, ChampObjectIndex objectIndex) {
+
+               graph.storeObjectIndex(objectIndex);
+               graph.storeObjectIndex(objectIndex); //Test storing an already existing object index
+
+               assertTrue(!graph.retrieveRelationshipIndex(objectIndex.getName()).isPresent()); //Make sure this wasn't stored as an object index
+
+               final Optional<ChampObjectIndex> retrieveObjectIndex = graph.retrieveObjectIndex(objectIndex.getName());
+               
+               if (!retrieveObjectIndex.isPresent()) throw new AssertionError("Failed to retrieve object index after storing it");
+               if (!objectIndex.equals(retrieveObjectIndex.get())) throw new AssertionError("Non-equal object index returned from API after storing it");
+               
+               final Stream<ChampObjectIndex> retrievedObjectIndices = graph.retrieveObjectIndices();
+               final Collection<ChampObjectIndex> allObjectIndices = retrievedObjectIndices.collect(Collectors.toList());
+
+               if (!allObjectIndices.contains(objectIndex)) throw new AssertionError("Retrieve all indices did not contained index previously stored");
+               if (allObjectIndices.size() != 1) throw new AssertionError("Wrong number of indices returned by retrieve all indices");
+
+               assertTrue(!graph.retrieveObjectIndex("nonExistentIndexName").isPresent());
+       }
+
+       @Test
+       public void testFluentRelationshipCreation() {
+               final ChampObjectIndex objectIndex = ChampObjectIndex.create()
+                                                                                                                               .ofName("fooNameIndex")
+                                                                                                                               .onType("foo")
+                                                                                                                               .forField("name")
+                                                                                                                               .build();
+
+               assertTrue(objectIndex.getName().equals("fooNameIndex"));
+               assertTrue(objectIndex.getType().equals("foo"));
+               assertTrue(objectIndex.getField().getName().equals("name"));
+       }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampObjectTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampObjectTest.java
new file mode 100644 (file)
index 0000000..f149243
--- /dev/null
@@ -0,0 +1,331 @@
+/**
+ * ============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.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampCardinality;
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampSchema;
+
+public class ChampObjectTest extends BaseChampAPITest {
+
+       @Test
+       public void testHashCode() {
+               final ChampObject foo1 = ChampObject.create()
+                                       .ofType("foo")
+                                       .withoutKey()
+                                       .withProperty("property", "value")
+                                       .withProperty("prop", 1)
+                                       .build();
+
+               final ChampObject foo2 = ChampObject.create()
+                                                                                       .ofType("foo")
+                                                                                       .withoutKey()
+                                                                                       .withProperty("property", "value")
+                                                                                       .withProperty("prop", 1)
+                                                                                       .build();
+
+               final ChampObject foo1Copy = ChampObject.create()
+                                                                                               .from(foo1)
+                                                                                               .withoutKey()
+                                                                                               .build();
+
+               final ChampObject foo2Copy = ChampObject.create()
+                                                                                               .from(foo2)
+                                                                                               .withoutKey()
+                                                                                               .build();
+
+               assertTrue(foo1.hashCode() == foo2.hashCode());
+               assertTrue(foo1.hashCode() == foo1.hashCode());
+               assertTrue(foo2.hashCode() == foo2.hashCode());
+               assertTrue(foo1.hashCode() == foo1Copy.hashCode());
+               assertTrue(foo2.hashCode() == foo2Copy.hashCode());
+               
+               assertTrue(Collections.singleton(foo1).contains(foo1));
+               assertTrue(Collections.singleton(foo1).contains(foo1Copy));
+       }
+
+       @Test
+       public void runTest() {
+               for (ChampGraph.Type apiType : ChampGraph.Type.values()) {
+                       final String graphName = ChampObjectTest.class.getSimpleName();
+                       switch (apiType) {
+                               case TITAN:
+                                       cleanUp(graphName);
+                                       break;
+                               default:
+                               break;
+                       }
+
+                       final ChampAPI api = ChampAPI.Factory.newInstance(apiType);
+                       ChampObjectTest.testChampObjectCrud(api.getGraph(graphName));
+                       testChampObjectReservedProperties(api.getGraph(graphName));
+                       api.shutdown();
+               }
+       }
+       
+       public static void testChampObjectCrud(ChampGraph graph) {
+               final ChampObject bookooObject = ChampObject.create()
+                                                                                                       .ofType("foo")
+                                                                                                       .withoutKey()
+                                                                                                       .withProperty("property1", "value1")
+                                                                                                       .withProperty("integer",  1)
+                                                                                                       .withProperty("long", 1L)
+                                                                                                       .withProperty("double", 1.2)
+                                                                                                       .withProperty("float", 2.3F)
+                                                                                                       .withProperty("string", "foo")
+                                                                                                       .withProperty("boolean", true)
+                                                                                                       .withProperty("list", Collections.singletonList("list"))
+                                                                                                       .withProperty("set", Collections.singleton("set"))
+                                                                                                       .build();
+
+               final ChampObject storedBookooObject;
+
+               try {
+
+                       graph.storeSchema(ChampSchema.create()
+                                                                                       .withObjectConstraint()
+                                                                                               .onType("foo")
+                                                                                               .withPropertyConstraint()
+                                                                                                       .onField("list")
+                                                                                                       .ofType(ChampField.Type.STRING)
+                                                                                                       .cardinality(ChampCardinality.LIST)
+                                                                                                       .optional()
+                                                                                                       .build()
+                                                                                               .withPropertyConstraint()
+                                                                                                       .onField("set")
+                                                                                                       .ofType(ChampField.Type.STRING)
+                                                                                                       .cardinality(ChampCardinality.SET)
+                                                                                                       .optional()
+                                                                                                       .build()
+                                                                                               .build()
+                                                                                       .build());
+
+                       storedBookooObject = graph.storeObject(bookooObject);
+
+                       assertTrue(storedBookooObject.getProperty("property1").get().equals("value1"));
+                       assertTrue(storedBookooObject.getProperty("integer").get().equals(1));
+                       assertTrue(storedBookooObject.getProperty("long").get().equals(1L));
+                       assertTrue(storedBookooObject.getProperty("double").get().equals(1.2));
+                       assertTrue(storedBookooObject.getProperty("float").get().equals(2.3F));
+                       assertTrue(storedBookooObject.getProperty("string").get().equals("foo"));
+                       assertTrue(storedBookooObject.getProperty("boolean").get().equals(true));
+                       assertTrue(storedBookooObject.getProperty("list").get().equals(Collections.singletonList("list")));
+                       assertTrue(storedBookooObject.getProperty("set").get().equals(Collections.singleton("set")));
+
+                       final Optional<ChampObject> retrievedBookooObject = graph.retrieveObject(storedBookooObject.getKey().get());
+                       final Stream<ChampObject> emptyStream = graph.queryObjects(new HashMap<String, Object> () {{
+                                                                                                                                                               put(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString(), "foo");
+                                                                                                                                                               put("long", 2L);
+                                                                                                                                                       }});
+                       
+                       assertTrue(emptyStream.limit(1).count() == 0);
+
+                       final Stream<ChampObject> oneStream = graph.queryObjects(new HashMap<String, Object> () {{
+                                                                                                                                                               put(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString(), "foo");
+                                                                                                                                                               put("long", 1L);
+                                                                                                                                                       }});
+                       final List<ChampObject> oneObject = oneStream.limit(2).collect(Collectors.toList());
+                       assertTrue(oneObject.size() == 1);
+                       assertTrue(oneObject.get(0).equals(storedBookooObject));
+
+                       final List<ChampObject> queryByKey = graph.queryObjects(Collections.singletonMap(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_KEY.toString(), storedBookooObject.getKey().get()))
+                                                                                                               .limit(2)
+                                                                                                               .collect(Collectors.toList());
+
+                       assertTrue(queryByKey.size() == 1);
+                       assertTrue(queryByKey.get(0).equals(storedBookooObject));
+
+                       if (!retrievedBookooObject.isPresent()) throw new AssertionError("Failed to retrieve stored object " + bookooObject);
+                       if (!storedBookooObject.equals(retrievedBookooObject.get())) throw new AssertionError("Retrieved object does not equal stored object");
+
+                       final ChampObject updatedBookoo = graph.storeObject(ChampObject.create()
+                                                                                                       .from(storedBookooObject)
+                                                                                                       .withKey(storedBookooObject.getKey().get())
+                                                                                                       .withProperty("long", 2L)
+                                                                                                       .build());
+
+                       final Optional<ChampObject> retrievedUpdBookooObject = graph.retrieveObject(updatedBookoo.getKey().get());
+
+                       assertTrue(updatedBookoo.getProperty("property1").get().equals("value1"));
+                       assertTrue(updatedBookoo.getProperty("integer").get().equals(1));
+                       assertTrue(updatedBookoo.getProperty("long").get().equals(2L));
+                       assertTrue(updatedBookoo.getProperty("double").get().equals(1.2));
+                       assertTrue(updatedBookoo.getProperty("float").get().equals(2.3F));
+                       assertTrue(updatedBookoo.getProperty("string").get().equals("foo"));
+                       assertTrue(updatedBookoo.getProperty("boolean").get().equals(true));
+                       assertTrue(updatedBookoo.getProperty("list").get().equals(Collections.singletonList("list")));
+                       assertTrue(updatedBookoo.getProperty("set").get().equals(Collections.singleton("set")));
+
+                       if (!retrievedUpdBookooObject.isPresent()) throw new AssertionError("Failed to retrieve stored object " + bookooObject);
+                       if (!updatedBookoo.equals(retrievedUpdBookooObject.get())) throw new AssertionError("Retrieved object does not equal stored object");
+                       
+                       //validate the replaceObject method
+                       final ChampObject replacedBookoo = graph.replaceObject(ChampObject.create()
+                                       .ofType("foo")
+                                       .withKey(storedBookooObject.getKey().get())
+                                       .withProperty("property1", "value2")
+                                       .withProperty("list", Collections.singletonList("list"))
+                                       .withProperty("set", Collections.singleton("set"))
+                                       .build());
+                       
+                       final Optional<ChampObject> retrievedReplacedBookooObject = graph.retrieveObject(replacedBookoo.getKey().get());
+                       
+                       assertTrue(replacedBookoo.getProperties().size()==3);
+                       assertTrue(replacedBookoo.getProperty("property1").get().equals("value2"));
+                       assertTrue(replacedBookoo.getProperty("list").get().equals(Collections.singletonList("list")));
+                       assertTrue(replacedBookoo.getProperty("set").get().equals(Collections.singleton("set")));
+                       
+                       
+                       if (!retrievedReplacedBookooObject.isPresent()) throw new AssertionError("Failed to retrieve stored object " + replacedBookoo);
+                       if (!replacedBookoo.equals(retrievedReplacedBookooObject.get())) throw new AssertionError("Retrieved object does not equal stored object");
+                       
+
+                       
+                       
+                       
+                       graph.deleteObject(storedBookooObject.getKey().get());
+                       if (graph.retrieveObject(storedBookooObject.getKey().get()).isPresent()) throw new AssertionError("Object not successfully deleted");
+
+                       assertTrue(graph.queryObjects(Collections.emptyMap()).count() == 0);
+                       assertTrue(graph.queryRelationships(Collections.emptyMap()).count() == 0);
+               } catch (ChampSchemaViolationException e) {
+                       throw new AssertionError("Schema mismatch while storing object", e);
+               } catch (ChampMarshallingException e) {
+                       throw new AssertionError("Marshalling exception while storing object", e);
+               } catch (ChampUnmarshallingException e) {
+                       throw new AssertionError("Unmarshalling exception while retrieving object", e);
+               }catch (ChampObjectNotExistsException e) {
+                       throw new AssertionError("Missing object on delete/update", e);
+               }
+
+               try {
+                       graph.deleteObject(storedBookooObject.getKey().get());
+                       throw new AssertionError("Delete succeeded when it should have failed");
+               } catch (ChampObjectNotExistsException e) {
+                       //Expected
+               }
+
+               try {
+                       graph.storeObject(ChampObject.create()
+                                                                                       .ofType("foo")
+                                                                                       .withKey("non-existent object key")
+                                                                                       .build());
+                       throw new AssertionError("Expected ChampObjectNotExistsException but object was successfully stored");
+               } catch (ChampObjectNotExistsException e) {
+                       //Expected
+               } catch (ChampMarshallingException e) {
+                       throw new AssertionError(e);
+               } catch (ChampSchemaViolationException e) {
+                       throw new AssertionError(e);
+               }
+               
+               try {
+                       // validate the replaceObject method when Object key is not passed
+                       graph.replaceObject(
+                                       ChampObject.create().ofType("foo").withoutKey().withProperty("property1", "value2").build());
+               } catch (ChampObjectNotExistsException e) {
+                       // Expected
+               } catch (ChampMarshallingException e) {
+                       throw new AssertionError(e);
+               } catch (ChampSchemaViolationException e) {
+                       throw new AssertionError(e);
+               }
+               
+       }
+
+       public void testChampObjectReservedProperties(ChampGraph graph) {
+
+               for (ChampObject.ReservedPropertyKeys key : ChampObject.ReservedPropertyKeys.values()) {
+                       try {
+                               ChampObject.create()
+                                                       .ofType(ChampObject.ReservedTypes.ANY.toString())
+                                                       .withoutKey()
+                                                       .withProperty(key.toString(), "")
+                                                       .build();
+                               throw new AssertionError("Allowed reserved property key to be used during object creation");
+                       } catch (IllegalArgumentException e) {
+                               //Expected
+                       }
+               }
+       }
+
+       @Test
+       public void testFluentObjectCreation() {
+               final Object value1 = new Object();
+               final String value2 = "value2";
+               final float value3 = 0.0f;
+
+               final ChampObject champObject1 = ChampObject.create()
+                                                                                                       .ofType("foo")
+                                                                                                       .withoutKey()
+                                                                                                       .withProperty("key1", value1)
+                                                                                                       .withProperty("key2", value2)
+                                                                                                       .withProperty("key3", value3)
+                                                                                                       .build();
+
+               assertTrue(champObject1.getKey().equals(Optional.empty()));
+               assertTrue(champObject1.getKey().isPresent() == false);
+               assertTrue(champObject1.getType().equals("foo"));
+               assertTrue(champObject1.getProperty("key1").get() instanceof Object);
+               assertTrue(champObject1.getProperty("key1").get().equals(value1));
+               assertTrue(champObject1.getProperty("key2").get() instanceof String);
+               assertTrue(champObject1.getProperty("key2").get().equals(value2));
+               assertTrue(champObject1.getProperty("key3").get() instanceof Float);
+               assertTrue(champObject1.getProperty("key3").get().equals(value3));
+
+               final ChampObject champObject2 = ChampObject.create()
+                                                                                                       .ofType("foo")
+                                                                                                       .withKey(1)
+                                                                                                       .withProperty("key1", value1)
+                                                                                                       .withProperty("key2", value2)
+                                                                                                       .withProperty("key3", value3)
+                                                                                                       .build();
+
+               assertTrue(champObject2.getType().equals("foo"));
+               assertTrue(champObject2.getKey().isPresent() == true);
+               assertTrue(champObject2.getKey().get() instanceof Integer);
+               assertTrue(champObject2.getKey().get().equals(1));
+               assertTrue(champObject2.getProperty("key1").get() instanceof Object);
+               assertTrue(champObject2.getProperty("key1").get().equals(value1));
+               assertTrue(champObject2.getProperty("key2").get() instanceof String);
+               assertTrue(champObject2.getProperty("key2").get().equals(value2));
+               assertTrue(champObject2.getProperty("key3").get() instanceof Float);
+               assertTrue(champObject2.getProperty("key3").get().equals(value3));
+       }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampPartitionTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampPartitionTest.java
new file mode 100644 (file)
index 0000000..c8671bb
--- /dev/null
@@ -0,0 +1,195 @@
+/**
+ * ============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.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Optional;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+
+public class ChampPartitionTest extends BaseChampAPITest {
+
+       @Test
+       public void runTests() {
+               for (ChampGraph.Type apiType : ChampGraph.Type.values()) {
+                       final ChampAPI api = ChampAPI.Factory.newInstance(apiType);
+                       final String graphName = ChampPartitionTest.class.getSimpleName();
+
+                       switch (apiType) {
+                       case IN_MEMORY:
+                       break;
+                       case TITAN:
+                               cleanUp(graphName);
+                       break;
+                       default:
+                       break;
+                       }
+
+                       ChampPartitionTest.testChampPartitionCrud(api.getGraph(graphName));
+                       api.shutdown();
+               }
+       }
+
+       @Test
+       public void testHashCode() {
+
+               final ChampObject foo = ChampObject.create()
+                                                                                               .ofType("foo")
+                                                                                               .withoutKey()
+                                                                                               .build();
+               final ChampObject bar = ChampObject.create()
+                                                                                               .ofType("bar")
+                                                                                               .withoutKey()
+                                                                                               .build();
+               final ChampRelationship baz = ChampRelationship.create()
+                                                                                                               .ofType("baz")
+                                                                                                               .withoutKey()
+                                                                                                               .withSource()
+                                                                                                                       .from(foo)
+                                                                                                                       .build()
+                                                                                                               .withTarget()
+                                                                                                                       .from(bar)
+                                                                                                                       .build()
+                                                                                                               .build();
+
+               final ChampPartition partition = ChampPartition.create()
+                                                                                                               .withObject(foo)
+                                                                                                               .withObject(bar)
+                                                                                                               .withRelationship(baz)
+                                                                                                               .build();
+
+               assertTrue(partition.getChampObjects().contains(foo));
+               assertTrue(partition.getChampObjects().contains(bar));
+               assertTrue(partition.getChampRelationships().contains(baz));
+       }
+
+       @Test
+       public void testBuilder() {
+               final ChampObject foo = new ChampObject.Builder("foo").build();
+               final ChampObject bar = new ChampObject.Builder("bar").build();
+               final ChampRelationship uses = new ChampRelationship.Builder(foo, bar, "uses")
+                                                                                                                               .build();
+               final ChampPartition a = new ChampPartition.Builder()
+                                                                                                       .object(foo)
+                                                                                                       .objects(Collections.singleton(bar))
+                                                                                                       .relationship(uses)
+                                                                                                       .relationships(Collections.singleton(uses))
+                                                                                                       .build();
+               assertTrue(a.getChampObjects().size() == 2);
+               assertTrue(a.getChampObjects().contains(foo));
+               assertTrue(a.getChampObjects().contains(bar));
+
+               assertTrue(a.getChampRelationships().size() == 1);
+               assertTrue(a.getChampRelationships().contains(uses));
+       }
+
+       public static void testChampPartitionCrud(ChampGraph graph) {
+
+               final ChampObject foo = ChampObject.create()
+                                                                                       .ofType("foo")
+                                                                                       .withoutKey()
+                                                                                       .withProperty("prop1", "value1")
+                                                                                       .build();
+               final ChampObject bar = ChampObject.create()
+                                                                                       .ofType("bar")
+                                                                                       .withoutKey()
+                                                                                       .withProperty("prop2", "value2")
+                                                                                       .build();
+
+               final ChampRelationship baz = ChampRelationship.create()
+                                                                                                               .ofType("baz")
+                                                                                                               .withoutKey()
+                                                                                                               .withSource()
+                                                                                                                       .from(foo)
+                                                                                                                       .build()
+                                                                                                               .withTarget()
+                                                                                                                       .from(bar)
+                                                                                                                       .build()
+                                                                                                               .withProperty("prop3", "value3")
+                                                                                                               .build();
+
+               final ChampPartition partition = ChampPartition.create()
+                                                                                                               .withObject(foo)
+                                                                                                               .withObject(bar)
+                                                                                                               .withRelationship(baz)
+                                                                                                               .build();
+
+               assertTrue(partition.getIncidentRelationships(foo).contains(baz));
+               assertTrue(partition.getIncidentRelationships(bar).contains(baz));
+               assertTrue(partition.getIncidentRelationshipsByType(foo).get("baz").contains(baz));
+
+               try {
+                       final ChampPartition storedPartition = graph.storePartition(partition);
+
+                       ChampPartitionTest.retrievePartitionElements(graph, storedPartition, true);
+
+                       graph.deletePartition(storedPartition);
+
+                       ChampPartitionTest.retrievePartitionElements(graph, storedPartition, false);
+
+               } catch (ChampMarshallingException e) {
+                       throw new AssertionError(e);
+               } catch (ChampObjectNotExistsException e) {
+                       throw new AssertionError(e);
+               } catch (ChampSchemaViolationException e) {
+                       throw new AssertionError(e);
+               } catch (ChampRelationshipNotExistsException e) {
+                       throw new AssertionError(e);
+               }
+       }
+
+       private static void retrievePartitionElements(ChampGraph graph, ChampPartition partition, boolean expectFound) {
+               for (ChampObject object : partition.getChampObjects()) {
+                       try {
+                               final Optional<ChampObject> retrievedObject = graph.retrieveObject(object.getKey().get());
+                               
+                               if (!expectFound && retrievedObject.isPresent()) throw new AssertionError("Expected object to not be found, but it was found");
+                               if (expectFound && !retrievedObject.isPresent()) throw new AssertionError("Expected object to be found, but it was not found");
+                       } catch (ChampUnmarshallingException e) {
+                               throw new AssertionError(e);
+                       }
+               }
+
+               for (ChampRelationship relationship : partition.getChampRelationships()) {
+                       try {
+                               final Optional<ChampRelationship> retrievedRelationship = graph.retrieveRelationship(relationship.getKey().get());
+                               
+                               if (!expectFound && retrievedRelationship.isPresent()) throw new AssertionError("Expected relationship to not be found, but it was found");
+                               if (expectFound && !retrievedRelationship.isPresent()) throw new AssertionError("Expected relationship to be found, but it was not found");
+                       } catch (ChampUnmarshallingException e) {
+                               throw new AssertionError(e);
+                       }
+               }
+       }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampPropertyConstraintTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampPropertyConstraintTest.java
new file mode 100644 (file)
index 0000000..8ee45f4
--- /dev/null
@@ -0,0 +1,57 @@
+/**
+ * ============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.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.model.ChampCardinality;
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampPropertyConstraint;
+
+public class ChampPropertyConstraintTest {
+
+       @Test
+       public void testChampPropertyConstraint() {
+               final ChampField z = new ChampField.Builder("z").build();
+               final ChampField y = new ChampField.Builder("y").build();
+
+               final ChampPropertyConstraint a = new ChampPropertyConstraint.Builder(z)
+                                                                                                                                               .cardinality(ChampCardinality.SINGLE)
+                                                                                                                                               .required(false)
+                                                                                                                                               .build();
+               final ChampPropertyConstraint aEquivalent = new ChampPropertyConstraint.Builder(z)
+                                                                                                                                                               .build();
+
+               final ChampPropertyConstraint b = new ChampPropertyConstraint.Builder(y)
+                                                                                                                                               .build();
+               assertTrue(a.equals(aEquivalent));
+               assertTrue(!a.equals(b));
+               assertTrue(!a.equals(new Object()));
+
+               assertTrue(a.toString().equals(aEquivalent.toString()));
+               assertTrue(!a.toString().equals(b.toString()));
+
+               assertTrue(a.compareTo(aEquivalent) == 0);
+               assertTrue(a.compareTo(b) != 0);
+       }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampRelationshipIndexTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampRelationshipIndexTest.java
new file mode 100644 (file)
index 0000000..a611df7
--- /dev/null
@@ -0,0 +1,175 @@
+/**
+ * ============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.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.exceptions.ChampIndexNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+
+public class ChampRelationshipIndexTest extends BaseChampAPITest {
+
+       @Test
+       public void runTest() {
+               for (ChampGraph.Type apiType : ChampGraph.Type.values()) {
+                       final String graphName = ChampRelationshipIndexTest.class.getSimpleName();
+
+                       switch (apiType) {
+                       case IN_MEMORY:
+                       break;
+                       case TITAN:
+                               cleanUp(graphName);
+                       break;
+                       default:
+                       break;
+                       }
+
+                       final ChampAPI api = ChampAPI.Factory.newInstance(apiType);
+                       testChampRelationshipIndexCrud(api.getGraph(graphName));
+                       api.shutdown();
+               }
+       }
+       
+       private void testChampRelationshipIndexCrud(ChampGraph graph) {
+       
+               final ChampField relationshipField = new ChampField.Builder("propertyName").build();
+               final ChampRelationshipIndex relationshipIndex = new ChampRelationshipIndex.Builder("fooEdgeIndex", "foo", relationshipField).build();
+
+               //Test on an empty graph
+               testChampRelationshipIndexStorage(graph, relationshipIndex);
+               testChampRelationshipIndexDelete(graph, relationshipIndex);
+
+               //Test with existing data in graph
+               try {
+                       graph.storeRelationship(ChampRelationship.create()
+                                                                                                       .ofType("uses")
+                                                                                                       .withoutKey()
+                                                                                                       .withSource()
+                                                                                                               .ofType("foo")
+                                                                                                               .withoutKey()
+                                                                                                               .build()
+                                                                                                       .withTarget()
+                                                                                                               .ofType("bar")
+                                                                                                               .withoutKey()
+                                                                                                               .build()
+                                                                                                       .build());
+                       testChampRelationshipIndexStorage(graph, relationshipIndex);
+                       testChampRelationshipIndexDelete(graph, relationshipIndex);
+               } catch (ChampMarshallingException e) {
+                       throw new AssertionError(e);
+               } catch (ChampSchemaViolationException e) {
+                       throw new AssertionError(e);
+               } catch (ChampObjectNotExistsException e) {
+                       throw new AssertionError(e);
+               } catch (ChampRelationshipNotExistsException e) {
+                       throw new AssertionError(e);
+               } catch (ChampUnmarshallingException e) {
+                       throw new AssertionError(e);
+               }
+       }
+
+       private void testChampRelationshipIndexDelete(ChampGraph graph, ChampRelationshipIndex relationshipIndex) {
+
+               if (!graph.capabilities().canDeleteRelationshipIndices()) {
+                       try {
+                               graph.deleteRelationshipIndex("someindex");
+                               throw new AssertionError("Graph claims it doesn't support relationship index delete, but it failed to throw UnsupportedOperationException");
+                       } catch (UnsupportedOperationException e) {
+                               //Expected
+                       } catch (ChampIndexNotExistsException e) {
+                               throw new AssertionError("Graph claims it doesn't support relationship index delete, but it failed to throw UnsupportedOperationException");
+                       }
+               } else {
+                       try {
+                               graph.deleteRelationshipIndex(relationshipIndex.getName());
+       
+                               final Optional<ChampRelationshipIndex> retrieveRelationshipIndex = graph.retrieveRelationshipIndex(relationshipIndex.getName());
+                               
+                               if (retrieveRelationshipIndex.isPresent()) throw new AssertionError("Retrieve relationship index after deleting it");
+       
+                               final Stream<ChampRelationshipIndex> relationshipIndices = graph.retrieveRelationshipIndices();
+                               final Collection<ChampRelationshipIndex> allRelationshipIndices = relationshipIndices.collect(Collectors.toList());
+       
+                               if (allRelationshipIndices.contains(relationshipIndex)) throw new AssertionError("Retrieve all relationship indices contains previously deleted index");
+                               if (allRelationshipIndices.size() != 0) throw new AssertionError("Wrong number of relationship indices returned by retrieve all indices");
+                       } catch (ChampIndexNotExistsException e) {
+                               throw new AssertionError(e);
+                       }
+       
+                       try {
+                               graph.deleteRelationshipIndex(relationshipIndex.getName());
+                               throw new AssertionError("Failed to throw exception on non-existent object index");
+                       } catch (ChampIndexNotExistsException e) {
+                               //Expected
+                       }
+               }
+       }
+
+       private void testChampRelationshipIndexStorage(ChampGraph graph, ChampRelationshipIndex relationshipIndex) {
+
+               graph.storeRelationshipIndex(relationshipIndex);
+               graph.storeRelationshipIndex(relationshipIndex); //Test storing duplicate relationship index
+
+               assertTrue(!graph.retrieveObjectIndex(relationshipIndex.getName()).isPresent()); //Make sure this wasn't stored as an object index
+
+               final Optional<ChampRelationshipIndex> retrieveRelationshipIndex = graph.retrieveRelationshipIndex(relationshipIndex.getName());
+               
+               if (!retrieveRelationshipIndex.isPresent()) throw new AssertionError("Failed to retrieve relationship index after storing it");
+               if (!relationshipIndex.equals(retrieveRelationshipIndex.get())) throw new AssertionError("Non-equal relationship index returned from API after storing it");
+
+               final Stream<ChampRelationshipIndex> relationshipIndices = graph.retrieveRelationshipIndices();
+               final Collection<ChampRelationshipIndex> allRelationshipIndices = relationshipIndices.collect(Collectors.toList());
+
+               if (!allRelationshipIndices.contains(relationshipIndex)) throw new AssertionError("Retrieve all relationship indices did not return previously stored relationship index");
+               if (allRelationshipIndices.size() != 1) throw new AssertionError("Wrong number of relationship indices returned by retrieve all indices");
+
+               assertTrue(!graph.retrieveRelationshipIndex("nonExistentIndexName").isPresent());
+       }
+
+       @Test
+       public void testFluentRelationshipIndexCreation() {
+               final ChampRelationshipIndex relationshipIndex = ChampRelationshipIndex.create()
+                                                                                                                               .ofName("fooNameIndex")
+                                                                                                                               .onType("foo")
+                                                                                                                               .forField("name")
+                                                                                                                               .build();
+
+               assertTrue(relationshipIndex.getName().equals("fooNameIndex"));
+               assertTrue(relationshipIndex.getType().equals("foo"));
+               assertTrue(relationshipIndex.getField().getName().equals("name"));
+       }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampRelationshipTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampRelationshipTest.java
new file mode 100644 (file)
index 0000000..cf025d6
--- /dev/null
@@ -0,0 +1,261 @@
+/**
+ * ============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.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationship.ReservedPropertyKeys;
+import org.openecomp.aai.champ.model.ChampRelationship.ReservedTypes;
+
+public class ChampRelationshipTest extends BaseChampAPITest {
+
+       @Test
+       public void runTest() {
+               for (ChampGraph.Type apiType : ChampGraph.Type.values()) {
+                       final String graphName = ChampRelationshipTest.class.getSimpleName();
+
+                       switch (apiType) {
+                               case IN_MEMORY:
+                               break;
+                               case TITAN:
+                                       cleanUp(graphName);
+                               break;
+                               default:
+                               break;
+                       }
+
+                       final ChampAPI api = ChampAPI.Factory.newInstance(apiType);
+                       ChampRelationshipTest.testChampRelationshipCrud(api.getGraph(graphName));
+                       api.shutdown();
+               }
+       }
+       
+       public static void testChampRelationshipCrud(ChampGraph graph) {
+               final ChampObject source = ChampObject.create()
+                                                                                               .ofType("foo")
+                                                                                               .withoutKey()
+                                                                                               .withProperty("property1", "value1")
+                                                                                               .build();
+
+               final ChampObject target = ChampObject.create()
+                                                                                               .ofType("foo")
+                                                                                               .withoutKey()
+                                                                                               .build();
+
+               try {
+                       final ChampObject storedSource = graph.storeObject(source);
+                       final ChampObject storedTarget = graph.storeObject(target);
+
+                       final ChampRelationship relationship = new ChampRelationship.Builder(storedSource, storedTarget, "relationship")
+                                                                                                                                       .property("property-1", "value-1")
+                                                                                                                                       .property("property-2", 3)
+                                                                                                                                       .build();
+
+                       final ChampRelationship storedRelationship = graph.storeRelationship(relationship);
+                       final Optional<ChampRelationship> retrievedRelationship = graph.retrieveRelationship(storedRelationship.getKey().get());
+
+                       if (!retrievedRelationship.isPresent()) throw new AssertionError("Failed to retrieve stored relationship " + storedRelationship);
+                       if (!storedRelationship.equals(retrievedRelationship.get())) throw new AssertionError("Retrieved relationship does not equal stored object");
+
+                       assertTrue(retrievedRelationship.get().getProperty("property-1").get().equals("value-1"));
+                       assertTrue(retrievedRelationship.get().getProperty("property-2").get().equals(3));
+
+                       if (!graph.retrieveRelationships(storedRelationship.getSource()).collect(Collectors.toList()).contains(storedRelationship))
+                               throw new AssertionError("Failed to retrieve relationships for source object");
+
+                       final ChampRelationship updatedRelationship = ChampRelationship.create()
+                                                                                                                                                       .from(retrievedRelationship.get())
+                                                                                                                                                       .withKey(retrievedRelationship.get().getKey().get())
+                                                                                                                                                       .withProperty("property-2", 4)
+                                                                                                                                                       .build();
+
+                       final ChampRelationship storedUpdRel = graph.storeRelationship(updatedRelationship);
+                       final Optional<ChampRelationship> retrievedUpdRel = graph.retrieveRelationship(storedUpdRel.getKey().get());
+
+                       assertTrue(retrievedUpdRel.isPresent());
+                       assertTrue(retrievedUpdRel.get().equals(storedUpdRel));
+                       assertTrue(retrievedUpdRel.get().getProperty("property-1").get().equals("value-1"));
+                       assertTrue(retrievedUpdRel.get().getProperty("property-2").get().equals(4));
+                       
+                       
+                       // validate the replaceRelationship method
+                       final ChampRelationship replacedRelationship = new ChampRelationship.Builder(storedSource, storedTarget, "relationship")
+                                       .key(retrievedRelationship.get().getKey().get())
+                                       .property("property-2", 4)
+                                       .build();
+
+                       final ChampRelationship replacedRel = graph.replaceRelationship(replacedRelationship);
+                       final Optional<ChampRelationship> retrievedReplacedRel = graph
+                                       .retrieveRelationship(replacedRel.getKey().get());
+                       
+                       assertTrue(replacedRel.getProperties().size()==1);
+                       assertTrue(replacedRel.getProperty("property-2").get().equals(4));
+                       
+                       assertTrue(retrievedReplacedRel.get().getProperties().size()==1);
+                       assertTrue(retrievedReplacedRel.get().getProperty("property-2").get().equals(4));
+                       
+                       if (!retrievedReplacedRel.isPresent()) throw new AssertionError("Failed to retrieve stored relationship " + replacedRel);
+                       if (!replacedRel.equals(retrievedReplacedRel.get())) throw new AssertionError("Retrieved relationship does not equal stored object");
+                       
+
+                       graph.deleteRelationship(retrievedRelationship.get());
+
+                       if (graph.retrieveRelationship(relationship.getKey()).isPresent()) throw new AssertionError("Relationship not successfully deleted");
+
+                       try {
+                               graph.deleteRelationship(retrievedRelationship.get());
+                               throw new AssertionError("Failed to throw exception for missing relationship");
+                       } catch (ChampRelationshipNotExistsException e) {
+                               //Expected
+                       }
+
+                       assertTrue(graph.queryRelationships(Collections.emptyMap()).count() == 0);
+                       assertTrue(graph.queryObjects(Collections.emptyMap()).count() == 2);
+               } catch (ChampSchemaViolationException e) {
+                       throw new AssertionError("Schema mismatch while storing object", e);
+               } catch (ChampMarshallingException e) {
+                       throw new AssertionError("Marshalling exception while storing object", e);
+               } catch (ChampUnmarshallingException e) {
+                       throw new AssertionError("Unmarshalling exception while retrieving relationship", e);
+               } catch (ChampRelationshipNotExistsException e) {
+                       throw new AssertionError("Attempted to delete non-existent relationship", e);
+               } catch (ChampObjectNotExistsException e) {
+                       throw new AssertionError("Object does not exist after storing it", e);
+               }
+
+               try {
+                       graph.retrieveRelationships(ChampObject.create().ofType("").withoutKey().build());
+                       throw new AssertionError("Failed to handle missing object while retrieving relationships");
+               } catch (ChampUnmarshallingException e) {
+                       throw new AssertionError(e);
+               } catch (ChampObjectNotExistsException e) {
+                       //Expected
+               }
+               //Negative test cases for replace relationship
+               
+               try{
+                       graph.replaceRelationship(new ChampRelationship.Builder(ChampObject.create()
+                                       .ofType("foo")
+                                       .withoutKey()
+                                       .build(), ChampObject.create()
+                                       .ofType("foo")
+                                       .withoutKey()
+                                       .build(), "relationship")
+                       .key("1234")
+                       .property("property-2", 4)
+                       .build());
+               }
+               catch (ChampUnmarshallingException e) {
+                       throw new AssertionError(e);
+               }  catch (ChampMarshallingException e) {
+                       throw new AssertionError(e);
+               } catch (ChampSchemaViolationException e) {
+                       throw new AssertionError(e);
+               } catch (ChampRelationshipNotExistsException e) {
+                       throw new AssertionError(e);
+               } catch(IllegalArgumentException e){
+               //expected      
+               }
+               
+               try{
+                       graph.replaceRelationship(new ChampRelationship.Builder(ChampObject.create()
+                                       .ofType("foo")
+                                       .withKey("123")
+                                       .build(), ChampObject.create()
+                                       .ofType("foo")
+                                       .withKey("456")
+                                       .build(), "relationship")                       
+                       .property("property-2", 4)
+                       .build());
+               }
+               catch (ChampUnmarshallingException e) {
+                       throw new AssertionError(e);
+               }  catch (ChampMarshallingException e) {
+                       throw new AssertionError(e);
+               } catch (ChampSchemaViolationException e) {
+                       throw new AssertionError(e);
+               } catch (ChampRelationshipNotExistsException e) {
+                       //expected
+               } catch(IllegalArgumentException e){
+                       throw new AssertionError(e);
+               }
+               
+               
+       }
+       
+       @Test
+       public void testFluentRelationshipCreation() {
+               final Object value1 = new Object();
+               final String value2 = "value2";
+               final float value3 = 0.0f;
+
+               final ChampRelationship champRelationship = ChampRelationship.create()
+                                                                                                       .ofType("foo")
+                                                                                                       .withoutKey()
+                                                                                                       .withSource()
+                                                                                                               .ofType("bar")
+                                                                                                               .withoutKey()
+                                                                                                               .build()
+                                                                                                       .withTarget()
+                                                                                                               .ofType("baz")
+                                                                                                               .withKey(1)
+                                                                                                               .build()
+                                                                                                       .withProperty("key1", value1)
+                                                                                                       .withProperty("key2", value2)
+                                                                                                       .withProperty("key3", value3)
+                                                                                                       .build();
+
+               assertTrue(champRelationship.getKey().equals(Optional.empty()));
+               assertTrue(champRelationship.getType().equals("foo"));
+               assertTrue(champRelationship.getProperty("key1").get() instanceof Object);
+               assertTrue(champRelationship.getProperty("key1").get().equals(value1));
+               assertTrue(champRelationship.getProperty("key2").get() instanceof String);
+               assertTrue(champRelationship.getProperty("key2").get().equals(value2));
+               assertTrue(champRelationship.getProperty("key3").get() instanceof Float);
+               assertTrue(champRelationship.getProperty("key3").get().equals(value3));
+       }
+
+       @Test
+       public void testChampRelationshipEnums() {
+               for (ReservedPropertyKeys key : ChampRelationship.ReservedPropertyKeys.values()) {
+                       assertTrue(ChampRelationship.ReservedPropertyKeys.valueOf(key.name()) == key);
+               }
+
+               for (ReservedTypes type : ChampRelationship.ReservedTypes.values()) {
+                       assertTrue(ChampRelationship.ReservedTypes.valueOf(type.name()) == type);
+               }
+       }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampSchemaTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampSchemaTest.java
new file mode 100644 (file)
index 0000000..e3f099e
--- /dev/null
@@ -0,0 +1,761 @@
+/**
+ * ============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.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Set;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.model.ChampConnectionConstraint;
+import org.openecomp.aai.champ.model.ChampConnectionMultiplicity;
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObject.ReservedTypes;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampPropertyConstraint;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.schema.AlwaysValidChampSchemaEnforcer;
+import org.openecomp.aai.champ.schema.ChampSchemaEnforcer;
+import org.openecomp.aai.champ.schema.DefaultChampSchemaEnforcer;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class ChampSchemaTest extends BaseChampAPITest {
+
+       @Test
+       public void runTest() {
+               for (ChampGraph.Type apiType : ChampGraph.Type.values()) {
+                       final String graphName = ChampSchemaTest.class.getSimpleName();
+
+                       switch (apiType) {
+                       case IN_MEMORY:
+                       break;
+                       case TITAN:
+                               cleanUp(graphName);
+                       break;
+                       default:
+                       break;
+                       }
+
+                       final ChampAPI api = ChampAPI.Factory.newInstance(apiType);
+
+                       try {
+                               ChampSchemaTest.testChampSchemaCrud(api.getGraph(graphName));
+                       } catch (Throwable t) {
+                               throw new AssertionError(apiType + " unit test failed", t);
+                       }
+
+                       api.shutdown();
+               }
+       }
+
+       public static void testChampSchemaCrud(ChampGraph graph) {
+
+               final ChampSchema schema = ChampSchema.create()
+                                       .withObjectConstraint()
+                                               .onType("foo")
+                                               .withPropertyConstraint()
+                                                       .onField("property1")
+                                                       .required()
+                                                       .build()
+                                               .withPropertyConstraint()
+                                                       .onField("property2")
+                                                       .optional()
+                                                       .build()
+                                               .build()
+                                       .withRelationshipConstraint()
+                                               .onType("bar")
+                                               .withPropertyConstraint()
+                                                       .onField("at")
+                                                       .ofType(ChampField.Type.STRING)
+                                                       .optional()
+                                                       .build()
+                                               .withConnectionConstraint()
+                                                       .sourcedFrom("foo")
+                                                       .targetedToAny()
+                                                       .build()
+                                               .build()
+                                       .build();
+
+               try {
+                       graph.storeSchema(schema);
+               } catch (ChampSchemaViolationException e) {
+                       throw new AssertionError(e);
+               }
+
+               final ChampObject emptyFoo = ChampObject.create()
+                                                                                               .ofType("foo")
+                                                                                               .withoutKey()
+                                                                                               .build();
+
+               try {
+                       graph.storeObject(emptyFoo);
+               } catch (ChampMarshallingException e1) {
+                       throw new AssertionError(e1);
+               } catch (ChampSchemaViolationException e1) {
+                       //Expected, since it does not have the required property "property1"
+               } catch (ChampObjectNotExistsException e) {
+                       throw new AssertionError(e);
+               }
+
+               final ChampSchema retrievedSchema = graph.retrieveSchema();
+
+               if (!schema.equals(retrievedSchema)) throw new AssertionError("Retrieved schema is not the same as the schema that was previously stored");
+
+               try {
+                       graph.updateSchema(new ChampRelationshipConstraint.Builder("bard").build());
+                       assertTrue(graph.retrieveSchema().getRelationshipConstraint("bard").isPresent());
+
+                       graph.updateSchema(new ChampObjectConstraint.Builder("baz").build());
+                       assertTrue(graph.retrieveSchema().getObjectConstraint("baz").isPresent());
+               } catch (ChampSchemaViolationException e) {
+                       throw new AssertionError(e);
+               }
+
+               final ChampSchema updatedSchema = graph.retrieveSchema();
+
+               if (!updatedSchema.getObjectConstraint("baz").isPresent()) throw new AssertionError("Updated schema and retrieved, but retrieved schema did not contain updates");
+               if (!updatedSchema.getRelationshipConstraint("bard").isPresent()) throw new AssertionError("Updated schema and retrieved, but retrieved schema did not contain updates");
+
+               try {
+                       graph.updateSchema(new ChampObjectConstraint.Builder("foo")
+                                                                                                               .constraint(
+                                                                                                                       new ChampPropertyConstraint.Builder(
+                                                                                                                               new ChampField.Builder("property2")
+                                                                                                                                                               .build()
+                                                                                                                       )
+                                                                                                                       .required(false)
+                                                                                                                       .build()
+                                                                                                               )
+                                                                                                               .build());
+
+                       final ChampObject storedEmptyFoo = graph.storeObject(emptyFoo);
+                       
+                       graph.deleteObject(storedEmptyFoo.getKey().get());
+               } catch (ChampMarshallingException e) {
+                       throw new AssertionError(e);
+               } catch (ChampSchemaViolationException e) {
+                       throw new AssertionError(e);
+               } catch (ChampObjectNotExistsException e) {
+                       throw new AssertionError(e);
+               }
+               
+               graph.deleteSchema();
+               assertTrue(graph.retrieveSchema().equals(ChampSchema.emptySchema()));
+       }
+
+       @Test
+       public void testChampSchemaFluentApi() {
+               final ChampSchema schema = ChampSchema.create()
+                                       .withObjectConstraint()
+                                               .onType("foo")
+                                               .withPropertyConstraint()
+                                                       .onField("bar")
+                                                       .ofType(ChampField.Type.STRING)
+                                                       .required()
+                                                       .build()
+                                               .withPropertyConstraint()
+                                                       .onField("baz")
+                                                       .ofType(ChampField.Type.BOOLEAN)
+                                                       .optional()
+                                                       .build()
+                                               .build()
+                                       .withRelationshipConstraint()
+                                               .onType("eats")
+                                               .withPropertyConstraint()
+                                                       .onField("at")
+                                                       .ofType(ChampField.Type.STRING)
+                                                       .required()
+                                                       .build()
+                                               .withPropertyConstraint()
+                                                       .onField("for")
+                                                       .optional()
+                                                       .build()
+                                               .withConnectionConstraint()
+                                                       .sourcedFrom("foo")
+                                                       .targetedTo("foo")
+                                                       .withMultiplicity(ChampConnectionMultiplicity.ONE)
+                                                       .build()
+                                               .withConnectionConstraint()
+                                                       .sourcedFrom("bar")
+                                                       .targetedTo("bar")
+                                                       .build()
+                                       .build()
+                               .build();
+
+               assertTrue(schema.getObjectConstraint("foo").get().getType().equals("foo"));
+               
+               for (ChampPropertyConstraint propConst : schema.getObjectConstraint("foo").get().getPropertyConstraints()) {
+                       if (propConst.getField().getName().equals("bar")) {
+                               assertTrue(propConst.getField().getJavaType().equals(String.class));
+                               assertTrue(propConst.isRequired());
+                       } else if (propConst.getField().getName().equals("baz")) {
+                               assertTrue(propConst.getField().getJavaType().equals(Boolean.class));
+                               assertTrue(!propConst.isRequired());
+                       } else {
+                               throw new AssertionError("Unknown property constraint found: " + propConst);
+                       }
+               }
+
+               assertTrue(schema.getRelationshipConstraint("eats").get().getType().equals("eats"));
+
+               for (ChampPropertyConstraint propConst : schema.getRelationshipConstraint("eats").get().getPropertyConstraints()) {
+                       if (propConst.getField().getName().equals("at")) {
+                               assertTrue(propConst.getField().getJavaType().equals(String.class));
+                               assertTrue(propConst.isRequired());
+                       } else if (propConst.getField().getName().equals("for")) {
+                               assertTrue(propConst.getField().getJavaType().equals(String.class));
+                               assertTrue(!propConst.isRequired());
+                       } else {
+                               throw new AssertionError("Unknown property constraint found: " + propConst);
+                       }
+               }
+
+               for (ChampConnectionConstraint connConst : schema.getRelationshipConstraint("eats").get().getConnectionConstraints()) {
+                       if (connConst.getSourceType().equals("foo")) {
+                               assertTrue(connConst.getTargetType().equals("foo"));
+                               assertTrue(connConst.getMultiplicity() == ChampConnectionMultiplicity.ONE);
+                       } else if (connConst.getSourceType().equals("bar")) {
+                               assertTrue(connConst.getTargetType().equals("bar"));
+                               assertTrue(connConst.getMultiplicity() == ChampConnectionMultiplicity.MANY);
+                       } else {
+                               throw new AssertionError("Unknown connection constraint found: " + connConst);
+                       }
+               }
+       }
+
+       @Test
+       public void testDefaultChampSchemaEnforcer() {
+
+               final ChampSchemaEnforcer schemaEnforcer = new DefaultChampSchemaEnforcer();
+               final ChampSchema champSchema = ChampSchema.create()
+                                                                                       .withObjectConstraint()
+                                                                                               .onType("foo")
+                                                                                               .withPropertyConstraint()
+                                                                                                       .onField("bar")
+                                                                                                       .ofType(ChampField.Type.STRING)
+                                                                                                       .required()
+                                                                                                       .build()
+                                                                                               .build()
+                                                                                       .withRelationshipConstraint()
+                                                                                               .onType("makes")
+                                                                                               .withPropertyConstraint()
+                                                                                                       .onField("bar")
+                                                                                                       .required()
+                                                                                                       .build()
+                                                                                               .withConnectionConstraint()
+                                                                                                       .sourcedFrom("foo")
+                                                                                                       .targetedTo("fiz")
+                                                                                                       .withMultiplicity(ChampConnectionMultiplicity.ONE)
+                                                                                                       .build()
+                                                                                               .build()
+                                                                                       .build();
+
+               try {
+                       schemaEnforcer.validate(ChampObject.create()
+                                                                                               .ofType("foo")
+                                                                                               .withoutKey()
+                                                                                               .withProperty("bar", "true")
+                                                                                               .build(),
+                                                                       champSchema.getObjectConstraint("foo").get());
+               } catch (ChampSchemaViolationException e) {
+                       throw new AssertionError(e);
+               }
+
+               try {
+                       schemaEnforcer.validate(ChampObject.create()
+                                                                                               .ofType("foo")
+                                                                                               .withoutKey()
+                                                                                               .build(),
+                                                                       champSchema.getObjectConstraint("foo").get());
+                       throw new AssertionError("Failed to enforce required property constraint on object");
+               } catch (ChampSchemaViolationException e) {
+                       //Expected
+               }
+
+               try {
+                       schemaEnforcer.validate(ChampObject.create()
+                                                                                               .ofType("foo")
+                                                                                               .withoutKey()
+                                                                                               .withProperty("bar", true)
+                                                                                               .build(),
+                                                                       champSchema.getObjectConstraint("foo").get());
+                       throw new AssertionError("Failed to enforce property type constraint on object");
+               } catch (ChampSchemaViolationException e) {
+                       //Expected
+               }
+
+               try {
+                       schemaEnforcer.validate(ChampRelationship.create()
+                                                                                               .ofType("makes")
+                                                                                               .withoutKey()
+                                                                                               .withSource()
+                                                                                                       .ofType("foo")
+                                                                                                       .withoutKey()
+                                                                                                       .build()
+                                                                                               .withTarget()
+                                                                                                       .ofType("fiz")
+                                                                                                       .withoutKey()
+                                                                                                       .build()
+                                                                                               .withProperty("bar", "true")
+                                                                                               .build(),
+                                                                       champSchema.getRelationshipConstraint("makes").get()
+                                                               );
+               } catch (ChampSchemaViolationException e) {
+                       throw new AssertionError(e);
+               }
+
+               try {
+                       schemaEnforcer.validate(ChampRelationship.create()
+                                                                                                               .ofType("makes")
+                                                                                                               .withoutKey()
+                                                                                                               .withSource()
+                                                                                                                       .ofType("foo")
+                                                                                                                       .withoutKey()
+                                                                                                                       .build()
+                                                                                                               .withTarget()
+                                                                                                                       .ofType("fiz")
+                                                                                                                       .withoutKey()
+                                                                                                                       .build()
+                                                                                                               .build(),
+                                                                       champSchema.getRelationshipConstraint("makes").get()
+                                                               );
+                       throw new AssertionError("Failed to enforce required property constraint on relationship");
+               } catch (ChampSchemaViolationException e) {
+                       //Expected
+               }
+
+               try {
+                       schemaEnforcer.validate(ChampPartition.create()
+                                                                                               .withObject(
+                                                                                                       ChampObject.create()
+                                                                                                                               .ofType("foo")
+                                                                                                                               .withoutKey()
+                                                                                                                               .withProperty("bar", "true")
+                                                                                                                               .build()
+                                                                                               )
+                                                                                               .withObject(
+                                                                                                       ChampObject.create()
+                                                                                                                               .ofType("fiz")
+                                                                                                                               .withoutKey()
+                                                                                                                               .build()
+                                                                                               )
+                                                                                               .withRelationship(
+                                                                                                       ChampRelationship.create()
+                                                                                                                                               .ofType("makes")
+                                                                                                                                               .withoutKey()
+                                                                                                                                               .withSource()
+                                                                                                                                                       .ofType("foo")
+                                                                                                                                                       .withoutKey()
+                                                                                                                                                       .withProperty("bar", "true")
+                                                                                                                                                       .build()
+                                                                                                                                               .withTarget()
+                                                                                                                                                       .ofType("fiz")
+                                                                                                                                                       .withoutKey()
+                                                                                                                                                       .build()
+                                                                                                                                               .withProperty("bar",  "true")
+                                                                                                                                               .build()
+                                                                                               )
+                                                                                               .withRelationship(
+                                                                                                       ChampRelationship.create()
+                                                                                                                                               .ofType("makes")
+                                                                                                                                               .withoutKey()
+                                                                                                                                               .withSource()
+                                                                                                                                                       .ofType("fiz")
+                                                                                                                                                       .withoutKey()
+                                                                                                                                                       .build()
+                                                                                                                                               .withTarget()
+                                                                                                                                                       .ofType("foo")
+                                                                                                                                                       .withoutKey()
+                                                                                                                                                       .withProperty("bar", "true")
+                                                                                                                                                       .build()
+                                                                                                                                               .withProperty("bar", "true")
+                                                                                                                                               .build()
+                                                                                               )
+                                                                                               .build(),
+                                                                                               champSchema                                     
+                                                               );
+               } catch (ChampSchemaViolationException e) {
+                       throw new AssertionError(e);
+               }
+
+               try {
+                       schemaEnforcer.validate(ChampPartition.create()
+                                                                                               .withObject(
+                                                                                                       ChampObject.create()
+                                                                                                                               .ofType("foo")
+                                                                                                                               .withoutKey()
+                                                                                                                               .withProperty("bar", "true")
+                                                                                                                               .build()
+                                                                                               )
+                                                                                               .withObject(
+                                                                                                       ChampObject.create()
+                                                                                                                               .ofType("fiz")
+                                                                                                                               .withoutKey()
+                                                                                                                               .build()
+                                                                                               )
+                                                                                               .withRelationship(
+                                                                                                       ChampRelationship.create()
+                                                                                                                                               .ofType("makes")
+                                                                                                                                               .withoutKey()
+                                                                                                                                               .withSource()
+                                                                                                                                                       .ofType("foo")
+                                                                                                                                                       .withoutKey()
+                                                                                                                                                       .withProperty("bar", "true")
+                                                                                                                                                       .build()
+                                                                                                                                               .withTarget()
+                                                                                                                                                       .ofType("fiz")
+                                                                                                                                                       .withoutKey()
+                                                                                                                                                       .build()
+                                                                                                                                               .withProperty("bar",  "true")
+                                                                                                                                               .build()
+                                                                                               )
+                                                                                               .withRelationship(
+                                                                                                       ChampRelationship.create()
+                                                                                                                                               .ofType("makes")
+                                                                                                                                               .withoutKey()
+                                                                                                                                               .withSource()
+                                                                                                                                                       .ofType("foo")
+                                                                                                                                                       .withoutKey()
+                                                                                                                                                       .withProperty("bar", "true")
+                                                                                                                                                       .build()
+                                                                                                                                               .withTarget()
+                                                                                                                                                       .ofType("fiz")
+                                                                                                                                                       .withoutKey()
+                                                                                                                                                       .build()
+                                                                                                                                               .withProperty("bar", "true")
+                                                                                                                                               .build()
+                                                                                               )
+                                                                                               .build(),
+                                                                                               champSchema                                     
+                                                               );
+                       throw new AssertionError("Failed to enforce connection constraint on relationship type 'makes'");
+               } catch (ChampSchemaViolationException e) {
+                       //Expected
+               }
+       }
+
+       @Test
+       public void testAlwaysValidChampSchemaEnforcer() {
+
+               final ChampSchemaEnforcer schemaEnforcer = new AlwaysValidChampSchemaEnforcer();
+
+               try {
+                       schemaEnforcer.validate(ChampObject.create()
+                                                                                               .ofType("foo")
+                                                                                               .withoutKey()
+                                                                                               .withProperty("bar", true)
+                                                                                               .build(),
+                                                                       new ChampObjectConstraint.Builder("foo")
+                                                                                                                               .constraint(
+                                                                                                                                       new ChampPropertyConstraint.Builder(
+                                                                                                                                               new ChampField.Builder("bar")
+                                                                                                                                                                               .type(ChampField.Type.STRING)
+                                                                                                                                                                               .build()
+                                                                                                                                       )
+                                                                                                                                       .required(true)
+                                                                                                                                       .build()
+                                                                                                                               )
+                                                                                                                               .build()
+                                                               );
+
+                       schemaEnforcer.validate(ChampRelationship.create()
+                                                                                               .ofType("foo")
+                                                                                               .withoutKey()
+                                                                                               .withSource()
+                                                                                                       .ofType("foo")
+                                                                                                       .withoutKey()
+                                                                                                       .build()
+                                                                                               .withTarget()
+                                                                                                       .ofType("fiz")
+                                                                                                       .withoutKey()
+                                                                                                       .build()
+                                                                                               .withProperty("bar", true)
+                                                                                               .build(),
+                                                                       new ChampRelationshipConstraint.Builder("bar")
+                                                                                                                               .constraint(
+                                                                                                                                       new ChampPropertyConstraint.Builder(
+                                                                                                                                               new ChampField.Builder("bar")
+                                                                                                                                                                               .type(ChampField.Type.STRING)
+                                                                                                                                                                               .build()
+                                                                                                                                       )
+                                                                                                                                       .required(true)
+                                                                                                                                       .build()
+                                                                                                                               )
+                                                                                                                               .build()
+                                                               );
+
+                       schemaEnforcer.validate(ChampPartition.create()
+                                                                                               .withObject(
+                                                                                                       ChampObject.create()
+                                                                                                                               .ofType("foo")
+                                                                                                                               .withoutKey()
+                                                                                                                               .withProperty("bar", true)
+                                                                                                                               .build()
+                                                                                               )
+                                                                                               .withObject(
+                                                                                                       ChampObject.create()
+                                                                                                                               .ofType("fiz")
+                                                                                                                               .withoutKey()
+                                                                                                                               .withProperty("bar", true)
+                                                                                                                               .build()
+                                                                                               )
+                                                                                               .withRelationship(
+                                                                                                       ChampRelationship.create()
+                                                                                                                                               .ofType("makes")
+                                                                                                                                               .withoutKey()
+                                                                                                                                               .withSource()
+                                                                                                                                                       .ofType("foo")
+                                                                                                                                                       .withoutKey()
+                                                                                                                                                       .build()
+                                                                                                                                               .withTarget()
+                                                                                                                                                       .ofType("fiz")
+                                                                                                                                                       .withoutKey()
+                                                                                                                                                       .build()
+                                                                                                                                               .build()
+                                                                                               )
+                                                                                               .withRelationship(
+                                                                                                       ChampRelationship.create()
+                                                                                                                                               .ofType("makes")
+                                                                                                                                               .withoutKey()
+                                                                                                                                               .withSource()
+                                                                                                                                                       .ofType("foo")
+                                                                                                                                                       .withoutKey()
+                                                                                                                                                       .build()
+                                                                                                                                               .withTarget()
+                                                                                                                                                       .ofType("fiz")
+                                                                                                                                                       .withoutKey()
+                                                                                                                                                       .build()
+                                                                                                                                               .withProperty("bar", true)
+                                                                                                                                               .build()
+                                                                                               )
+                                                                                               .build(),
+                                                               ChampSchema.create()
+                                                                                       .withObjectConstraint()
+                                                                                               .onType("foo")
+                                                                                               .withPropertyConstraint()
+                                                                                                       .onField("bar")
+                                                                                                       .required()
+                                                                                                       .build()
+                                                                                               .build()
+                                                                                       .withRelationshipConstraint()
+                                                                                               .onType("makes")
+                                                                                               .withPropertyConstraint()
+                                                                                                       .onField("bar")
+                                                                                                       .required()
+                                                                                                       .build()
+                                                                                               .withConnectionConstraint()
+                                                                                                       .sourcedFrom("foo")
+                                                                                                       .targetedTo("fiz")
+                                                                                                       .withMultiplicity(ChampConnectionMultiplicity.ONE)
+                                                                                                       .build()
+                                                                                               .build()
+                                                                                       .withRelationshipConstraint()
+                                                                                               .onType("uses")
+                                                                                               .withConnectionConstraint()
+                                                                                                       .sourcedFromAny()
+                                                                                                       .targetedTo("computer")
+                                                                                                       .build()
+                                                                                               .build()
+                                                                                       .withRelationshipConstraint()
+                                                                                               .onType("destroys")
+                                                                                               .withConnectionConstraint()
+                                                                                                       .sourcedFrom("computer")
+                                                                                                       .targetedToAny()
+                                                                                                       .build()
+                                                                                               .build()
+                                                                                       .build()
+                                                                                                       
+                                                               );
+               } catch (ChampSchemaViolationException e) {
+                       throw new AssertionError(e);
+               }
+       }
+
+       @Test
+       public void testFluentSchemaApi() {
+               final ChampSchema schema = ChampSchema.create()
+                                                                                               .withObjectConstraint()
+                                                                                                       .onType("a")
+                                                                                                       .withPropertyConstraint()
+                                                                                                               .onField("z")
+                                                                                                               .ofType(ChampField.Type.STRING)
+                                                                                                               .optional()
+                                                                                                               .build()
+                                                                                                       .build()
+                                                                                               .withObjectConstraint()
+                                                                                                       .onType("b")
+                                                                                                       .withPropertyConstraint()
+                                                                                                               .onField("y")
+                                                                                                               .ofType(ChampField.Type.LONG)
+                                                                                                               .required()
+                                                                                                               .build()
+                                                                                                       .build()
+                                                                                               .withRelationshipConstraint()
+                                                                                                       .onType("one")
+                                                                                                       .withPropertyConstraint()
+                                                                                                               .onField("nine")
+                                                                                                               .ofType(ChampField.Type.INTEGER)
+                                                                                                               .optional()
+                                                                                                               .build()
+                                                                                                       .withConnectionConstraint()
+                                                                                                               .sourcedFrom("a")
+                                                                                                               .targetedTo("b")
+                                                                                                               .withMultiplicity(ChampConnectionMultiplicity.NONE)
+                                                                                                               .build()
+                                                                                                       .withConnectionConstraint()
+                                                                                                               .sourcedFrom("a")
+                                                                                                               .targetedToAny()
+                                                                                                               .withMultiplicity(ChampConnectionMultiplicity.ONE)
+                                                                                                               .build()
+                                                                                                       .withConnectionConstraint()
+                                                                                                               .sourcedFromAny()
+                                                                                                               .targetedTo("b")
+                                                                                                               .withMultiplicity(ChampConnectionMultiplicity.MANY)
+                                                                                                               .build()
+                                                                                                       .withConnectionConstraint()
+                                                                                                               .sourcedFromAny()
+                                                                                                               .targetedToAny()
+                                                                                                               .withMultiplicity(ChampConnectionMultiplicity.MANY)
+                                                                                                               .build()
+                                                                                                       .build()
+                                                                                               .build();
+
+               final ChampObjectConstraint aObjConstraint = schema.getObjectConstraint("a").get();
+
+               assertTrue(aObjConstraint.getType().equals("a"));
+
+               final ChampPropertyConstraint zPropertyConstraint = aObjConstraint.getPropertyConstraint("z").get();
+
+               assertTrue(zPropertyConstraint.getField().getName().equals("z"));
+               assertTrue(zPropertyConstraint.getField().getJavaType().equals(String.class));
+               assertTrue(!zPropertyConstraint.isRequired());
+
+               final ChampObjectConstraint bObjConstraint = schema.getObjectConstraint("b").get();
+
+               assertTrue(bObjConstraint.getType().equals("b"));
+
+               final ChampPropertyConstraint yPropertyConstraint = bObjConstraint.getPropertyConstraint("y").get();
+
+               assertTrue(yPropertyConstraint.getField().getName().equals("y"));
+               assertTrue(yPropertyConstraint.getField().getJavaType().equals(Long.class));
+               assertTrue(yPropertyConstraint.isRequired());
+
+               final ChampRelationshipConstraint oneRelConstraint = schema.getRelationshipConstraint("one").get();
+
+               assertTrue(oneRelConstraint.getType().equals("one"));
+
+               final ChampPropertyConstraint ninePropertyConstraint = oneRelConstraint.getPropertyConstraint("nine").get();
+
+               assertTrue(ninePropertyConstraint.getField().getName().equals("nine"));
+               assertTrue(ninePropertyConstraint.getField().getJavaType().equals(Integer.class));
+               assertTrue(!ninePropertyConstraint.isRequired());
+
+               final Set<ChampConnectionConstraint> connectionConstraints = oneRelConstraint.getConnectionConstraints();
+
+               for (ChampConnectionConstraint cc : connectionConstraints) {
+                       if (cc.getSourceType().equals("a") && cc.getTargetType().equals("b")) {
+                               assertTrue(cc.getMultiplicity() == ChampConnectionMultiplicity.NONE);
+                       } else if (cc.getSourceType().equals(ReservedTypes.ANY.toString()) && cc.getTargetType().equals("b")) {
+                               assertTrue(cc.getMultiplicity() == ChampConnectionMultiplicity.MANY);
+                       } else if (cc.getSourceType().equals(ReservedTypes.ANY.toString()) && cc.getTargetType().equals(ReservedTypes.ANY.toString())) {
+                               assertTrue(cc.getMultiplicity() == ChampConnectionMultiplicity.MANY);
+                       } else if (cc.getSourceType().equals("a") && cc.getTargetType().equals(ReservedTypes.ANY.toString())) {
+                               assertTrue(cc.getMultiplicity() == ChampConnectionMultiplicity.ONE);
+                       } else {
+                               throw new AssertionError("Found unspecified connection constraint " + cc);
+                       }
+               }       
+       }
+
+       @Test
+       public void testJacksonObjectMapping() {
+               final ChampSchema schema = ChampSchema.create()
+                                                                                               .withObjectConstraint()
+                                                                                                       .onType("a")
+                                                                                                       .withPropertyConstraint()
+                                                                                                               .onField("z")
+                                                                                                               .ofType(ChampField.Type.STRING)
+                                                                                                               .optional()
+                                                                                                               .build()
+                                                                                                       .build()
+                                                                                               .withObjectConstraint()
+                                                                                                       .onType("b")
+                                                                                                       .withPropertyConstraint()
+                                                                                                               .onField("y")
+                                                                                                               .ofType(ChampField.Type.LONG)
+                                                                                                               .required()
+                                                                                                               .build()
+                                                                                                       .build()
+                                                                                               .withRelationshipConstraint()
+                                                                                                       .onType("one")
+                                                                                                       .withPropertyConstraint()
+                                                                                                               .onField("nine")
+                                                                                                               .ofType(ChampField.Type.INTEGER)
+                                                                                                               .optional()
+                                                                                                               .build()
+                                                                                                       .withConnectionConstraint()
+                                                                                                               .sourcedFrom("a")
+                                                                                                               .targetedTo("b")
+                                                                                                               .withMultiplicity(ChampConnectionMultiplicity.NONE)
+                                                                                                               .build()
+                                                                                                       .withConnectionConstraint()
+                                                                                                               .sourcedFrom("a")
+                                                                                                               .targetedToAny()
+                                                                                                               .withMultiplicity(ChampConnectionMultiplicity.ONE)
+                                                                                                               .build()
+                                                                                                       .withConnectionConstraint()
+                                                                                                               .sourcedFromAny()
+                                                                                                               .targetedTo("b")
+                                                                                                               .withMultiplicity(ChampConnectionMultiplicity.MANY)
+                                                                                                               .build()
+                                                                                                       .withConnectionConstraint()
+                                                                                                               .sourcedFromAny()
+                                                                                                               .targetedToAny()
+                                                                                                               .withMultiplicity(ChampConnectionMultiplicity.MANY)
+                                                                                                               .build()
+                                                                                                       .build()
+                                                                                               .build();
+
+               final ObjectMapper om = new ObjectMapper();
+
+               try {
+                       final byte[] serialized = om.writeValueAsBytes(schema);
+                       System.out.println(new String(serialized, "UTF-8"));
+                       final ChampSchema deserialized = om.readValue(serialized, ChampSchema.class);
+                       assert schema.equals(deserialized);
+               } catch (IOException e) {
+                       throw new AssertionError(e);
+               }
+
+       }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/event/AbstractLoggingChampGraphTest.java b/src/test/java/org/openecomp/aai/champ/event/AbstractLoggingChampGraphTest.java
new file mode 100644 (file)
index 0000000..5383d28
--- /dev/null
@@ -0,0 +1,1029 @@
+/**
+ * ============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.openecomp.aai.champ.event;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampCapabilities;
+import org.openecomp.aai.champ.exceptions.ChampIndexNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.slf4j.Logger;
+
+import com.att.nsa.cambria.client.CambriaPublisher;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+
+
+public class AbstractLoggingChampGraphTest {
+
+  /** Event stream producer stub. */
+  private InMemoryPublisher producer;
+  
+  /** In memory graph for testing purposes. */
+  private TestGraph testGraph;
+  
+  
+  /**
+   * Perform any setup tasks that need to be done prior to each test.
+   */
+  @Before
+  public void setup() {
+    
+    // Instantiate an event stream producer stub to use in our tests.
+    producer = new InMemoryPublisher();
+    
+    // Instantiate an 'in-memory' graph for test purposes.
+    Map<String, Object> graphProperties = new HashMap<String, Object>();
+    graphProperties.put("champ.event.stream.hosts", "myeventstreamhost");
+    graphProperties.put("champ.event.stream.batch-size", 1);
+    testGraph = new TestGraph(graphProperties, producer);
+  }
+  
+  
+  /**
+   * Perform any cleanup that needs to be done after each test.
+   */
+  @After
+  public void tearDown() {
+    
+    // Close our stubbed producer and graph.
+    producer.close();
+    testGraph.shutdown();
+  }
+  
+  /**
+   * Validates that store/replace/delete operation against vertices result in the expected events
+   * being published to the event stream.
+   * 
+   * @throws ChampMarshallingException
+   * @throws ChampSchemaViolationException
+   * @throws ChampObjectNotExistsException
+   * @throws InterruptedException
+   * @throws JsonParseException
+   * @throws JsonMappingException
+   * @throws IOException
+   */
+  @Test
+  public void vertexOperationsTest() throws ChampMarshallingException, 
+                                            ChampSchemaViolationException, 
+                                            ChampObjectNotExistsException, 
+                                            InterruptedException, 
+                                            JsonParseException, 
+                                            JsonMappingException, 
+                                            IOException {
+            
+    // Create a vertex and store it in the graph data store.
+    ChampObject obj1 = ChampObject.create()
+        .ofType("foo")
+        .withKey("123")
+        .withProperty("p1", "v1")
+        .withProperty("p2", "v2")
+        .build();  
+    testGraph.storeObject(obj1);
+
+    // Retrieve the next event from the event stream and validate that it is what we expect.
+    String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    assertTrue("Expected STORE event.", loggedEventStr.contains("STORE"));
+    assertTrue("Entity type for store event was not a vertex.", loggedEventStr.contains("vertex"));
+  
+    // Create a new vertex based on the one that we already created.
+    ChampObject obj2 = ChampObject.create()
+        .from(obj1)
+        .withKey("123")
+        .withProperty("p3", "v3")
+        .build();
+    
+    // Now, try doing a replace operation.
+    testGraph.replaceObject(obj2);
+    
+    // Retrieve the next event from the event stream and validate that it is what we expect.
+    loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    assertTrue("Expected REPLACE event.", loggedEventStr.contains("REPLACE"));
+    assertTrue("Entity type for store event was not a vertex.", loggedEventStr.contains("vertex"));
+    
+    // Finally, delete the vertex.
+    testGraph.deleteObject("123");
+    
+    // Retrieve the next event from the event stream and validate that it is what we expect.
+    loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    assertTrue("Expected DELETE event.", loggedEventStr.contains("DELETE"));
+    assertTrue("Entity type for store event was not a vertex.", loggedEventStr.contains("vertex"));
+  }
+  
+  
+  /**
+   * This test validates that performing vertex operations in the case where the data to be
+   * forwarded to the event stream is unavailable results in no event being generated, but
+   * does not otherwise create issues.
+   * 
+   * @throws ChampMarshallingException
+   * @throws ChampSchemaViolationException
+   * @throws ChampObjectNotExistsException
+   * @throws InterruptedException
+   * @throws JsonParseException
+   * @throws JsonMappingException
+   * @throws IOException
+   */
+  @Test
+  public void vertexOperationsWithNullsTest() throws ChampMarshallingException, 
+                                                     ChampSchemaViolationException, 
+                                                     ChampObjectNotExistsException, 
+                                                     InterruptedException, 
+                                                     JsonParseException, 
+                                                     JsonMappingException, 
+                                                     IOException {
+            
+    // Setup our test graph to simulate failures to retrieve data from the graph data store.
+    testGraph.returnNulls();
+    
+    // Create a vertex and store it in the graph data store.
+    ChampObject obj1 = ChampObject.create()
+        .ofType("foo")
+        .withKey("123")
+        .withProperty("p1", "v1")
+        .withProperty("p2", "v2")
+        .build();  
+    testGraph.storeObject(obj1);
+
+    // Check our simulated event stream to verify that an event log was produced.
+    String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    
+    // Validate that we did not get an event from the stream.
+    assertNull("Store vertex event should not have been logged to the event stream", loggedEventStr);
+    
+    // Create a new vertex based on the one that we already created.
+    ChampObject obj2 = ChampObject.create()
+        .from(obj1)
+        .withKey("123")
+        .withProperty("p3", "v3")
+        .build();
+    
+    // Now, try doing a replace operation.
+    testGraph.replaceObject(obj2);
+    
+    // Check our simulated event stream to see if an event log was not produced.
+    loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    
+    // Validate that we did not get an event from the stream.
+    assertNull("Store vertex event should not have been logged to the event stream", loggedEventStr);
+    
+    // Finally, delete the vertex.
+    testGraph.deleteObject("123");
+    
+    // Check our simulated event stream to see if an event log was not produced.
+    loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    
+    // Validate that we did not get an event from the stream.
+    assertNull("Store vertex event should not have been logged to the event stream", loggedEventStr);
+  }
+  
+  
+  /**
+   * Validates that store/replace/delete operation against edges result in the expected events
+   * being published to the event stream.
+   *
+   * @throws ChampMarshallingException
+   * @throws ChampSchemaViolationException
+   * @throws ChampObjectNotExistsException
+   * @throws InterruptedException
+   * @throws JsonParseException
+   * @throws JsonMappingException
+   * @throws IOException
+   * @throws ChampUnmarshallingException
+   * @throws ChampRelationshipNotExistsException
+   */
+  @Test
+  public void edgeOperationsTest() throws ChampMarshallingException, 
+                                          ChampSchemaViolationException, 
+                                          ChampObjectNotExistsException, 
+                                          InterruptedException, 
+                                          JsonParseException, 
+                                          JsonMappingException, 
+                                          IOException, 
+                                          ChampUnmarshallingException, 
+                                          ChampRelationshipNotExistsException {
+    
+    // Create two vertices to act as the end points of our edge.
+    ChampObject obj1 = ChampObject.create()
+        .ofType("foo")
+        .withKey("123")
+        .withProperty("p1", "v1")
+        .withProperty("p2", "v2")
+        .build();  
+
+    ChampObject obj2 = ChampObject.create()
+        .ofType("bar")
+        .withKey("123")
+        .withProperty("p3", "v3")
+        .build();
+    
+    // Now, create an edge object and write it to the graph data store.
+    ChampRelationship rel = new ChampRelationship.Builder(obj1, obj2, "relationship")
+        .property("property-1", "value-1")
+        .property("property-2", "value-2")
+        .build();
+    testGraph.storeRelationship(rel);
+    
+    // Retrieve the next event from the event stream and validate that it is what we expect.
+    String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    assertTrue("Expected STORE event.", loggedEventStr.contains("STORE"));
+    assertTrue("Entity type for store event was not an edge.", loggedEventStr.contains("relationship"));
+    
+    // Now, create another edge object based on the one we just wrote, and use it to perform
+    // a replace operation.
+    ChampRelationship rel2 = ChampRelationship.create()
+        .from(rel)
+        .withKey("123")
+        .withProperty("property-3", "value-3")
+        .build();
+    testGraph.replaceRelationship(rel2);
+
+    // Retrieve the next event from the event stream and validate that it is what we expect.
+    loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    assertTrue("Expected REPLACE event.", loggedEventStr.contains("REPLACE"));
+    assertTrue("Entity type for store event was not an edge.", loggedEventStr.contains("relationship"));
+    
+    // Finally, delete our edge.
+    testGraph.deleteRelationship(rel2);
+    
+    // Retrieve the next event from the event stream and validate that it is what we expect.
+    loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    assertTrue("Expected DELETE event.", loggedEventStr.contains("DELETE"));
+    assertTrue("Entity type for store event was not an edge.", loggedEventStr.contains("relationship"));
+  }
+  
+  
+  /**
+   * This test validates that performing edge operations in the case where the data to be
+   * forwarded to the event stream is unavailable results in no event being generated, but
+   * does not otherwise create issues.
+   * 
+   * @throws ChampMarshallingException
+   * @throws ChampSchemaViolationException
+   * @throws ChampObjectNotExistsException
+   * @throws InterruptedException
+   * @throws JsonParseException
+   * @throws JsonMappingException
+   * @throws IOException
+   * @throws ChampUnmarshallingException
+   * @throws ChampRelationshipNotExistsException
+   */
+  @Test
+  public void edgeOperationsWithNullsTest() throws ChampMarshallingException, 
+                                                   ChampSchemaViolationException, 
+                                                   ChampObjectNotExistsException, 
+                                                   InterruptedException, 
+                                                   JsonParseException, 
+                                                   JsonMappingException, 
+                                                   IOException, 
+                                                   ChampUnmarshallingException, 
+                                                   ChampRelationshipNotExistsException {
+    
+    // Set up our graph to simulate a failure to retrieve some of the data we need to generate
+    // events.
+    testGraph.returnNulls();
+    
+    // Create two vertices to act as the endpoints of our edge.
+    ChampObject obj1 = ChampObject.create()
+        .ofType("foo")
+        .withKey("123")
+        .withProperty("p1", "v1")
+        .withProperty("p2", "v2")
+        .build();  
+
+    ChampObject obj2 = ChampObject.create()
+        .ofType("bar")
+        .withKey("123")
+        .withProperty("p3", "v3")
+        .build();
+    
+    // Now, create an edge object and write it to the graph data store.
+    ChampRelationship rel = new ChampRelationship.Builder(obj1, obj2, "relationship")
+        .property("property-1", "value-1")
+        .property("property-2", "value-2")
+        .build();
+    testGraph.storeRelationship(rel);
+    
+    // Check our simulated event stream to see if an event log was produced.
+    String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    
+    // Validate that we did not get an event from the stream.
+    assertNull("Store edge event should not have been logged to the event stream", loggedEventStr);
+        
+    // Now, create another edge object based on the one we just wrote, and use it to perform
+    // a replace operation.
+    ChampRelationship rel2 = ChampRelationship.create()
+        .from(rel)
+        .withKey("123")
+        .withProperty("property-3", "value-3")
+        .build();
+    testGraph.replaceRelationship(rel2);
+    
+    // Check our simulated event stream to see if an event log was produced.
+    loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    
+    // Validate that we did not get an event from the stream.
+    assertNull("Store edge event should not have been logged to the event stream", loggedEventStr);   
+  }
+  
+  
+  /**
+   * Validates that store/replace/delete operation against partitions result in the expected events
+   * being published to the event stream.
+   *
+   * @throws ChampMarshallingException
+   * @throws ChampSchemaViolationException
+   * @throws ChampObjectNotExistsException
+   * @throws InterruptedException
+   * @throws JsonParseException
+   * @throws JsonMappingException
+   * @throws IOException
+   * @throws ChampUnmarshallingException
+   * @throws ChampRelationshipNotExistsException
+   */
+  @Test
+  public void partitionOperationsTest() throws ChampMarshallingException, 
+                                               ChampSchemaViolationException, 
+                                               ChampObjectNotExistsException, 
+                                               InterruptedException, 
+                                               JsonParseException, 
+                                               JsonMappingException, 
+                                               IOException, 
+                                               ChampUnmarshallingException, 
+                                               ChampRelationshipNotExistsException {
+    
+    // Create the vertices and edge objects that we need to create a partition.
+    ChampObject obj1 = ChampObject.create()
+        .ofType("foo")
+        .withKey("123")
+        .withProperty("p1", "v1")
+        .withProperty("p2", "v2")
+        .build();  
+
+    ChampObject obj2 = ChampObject.create()
+        .ofType("bar")
+        .withKey("123")
+        .withProperty("p3", "v3")
+        .build();
+    
+    // Now, create an edge object and write it to the graph data store.
+    ChampRelationship rel = new ChampRelationship.Builder(obj1, obj2, "relationship")
+        .property("property-1", "value-1")
+        .property("property-2", "value-2")
+        .build();
+    
+    // Now, create our partition object and store it in the graph.
+    ChampPartition partition = ChampPartition.create()
+        .withObject(obj1)
+        .withObject(obj2)
+        .withRelationship(rel)
+        .build();
+    testGraph.storePartition(partition);
+    
+    // Retrieve the next event from the event stream and validate that it is what we expect.
+    String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    assertTrue("Expected STORE event.", loggedEventStr.contains("STORE"));
+    assertTrue("Entity type for store event was not a partition.", loggedEventStr.contains("partition"));
+
+    // Now, delete our partition.
+    testGraph.deletePartition(partition);
+    
+    // Retrieve the next event from the event stream and validate that it is what we expect.
+    loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    assertTrue("Expected DELETE event.", loggedEventStr.contains("DELETE"));
+    assertTrue("Entity type for store event was not a partition.", loggedEventStr.contains("partition"));
+  }
+  
+  
+  /**
+   * This test validates that performing partition operations in the case where the data to be
+   * forwarded to the event stream is unavailable results in no event being generated, but
+   * does not otherwise create issues.
+   * 
+   * @throws ChampMarshallingException
+   * @throws ChampSchemaViolationException
+   * @throws ChampObjectNotExistsException
+   * @throws InterruptedException
+   * @throws JsonParseException
+   * @throws JsonMappingException
+   * @throws IOException
+   * @throws ChampUnmarshallingException
+   * @throws ChampRelationshipNotExistsException
+   */
+  @Test
+  public void partitionOperationsWithNullsTest() throws ChampMarshallingException, 
+                                          ChampSchemaViolationException, 
+                                          ChampObjectNotExistsException, 
+                                          InterruptedException, 
+                                          JsonParseException, 
+                                          JsonMappingException, 
+                                          IOException, 
+                                          ChampUnmarshallingException, 
+                                          ChampRelationshipNotExistsException {
+    
+    // Set up our graph to simulate a failure to retrieve some of the data we need to generate
+    // events.
+    testGraph.returnNulls();
+    
+    // Create all of the objects we need to create a partition, and store the partition
+    // in the graph.
+    ChampObject obj1 = ChampObject.create()
+        .ofType("foo")
+        .withKey("123")
+        .withProperty("p1", "v1")
+        .withProperty("p2", "v2")
+        .build();  
+
+    ChampObject obj2 = ChampObject.create()
+        .ofType("bar")
+        .withKey("123")
+        .withProperty("p3", "v3")
+        .build();
+    
+    ChampRelationship rel = new ChampRelationship.Builder(obj1, obj2, "relationship")
+        .property("property-1", "value-1")
+        .property("property-2", "value-2")
+        .build();
+    
+    ChampPartition partition = ChampPartition.create()
+        .withObject(obj1)
+        .withObject(obj2)
+        .withRelationship(rel)
+        .build();
+    testGraph.storePartition(partition);
+    
+    // Check our simulated event stream to see if an an event log was produced.
+    String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    
+    // Validate that we did not get an event from the stream.
+    assertNull("Store partition event should not have been logged to the event stream", loggedEventStr);
+  }
+  
+  
+  /**
+   * Validates that store/replace/delete operation against vertex indexes result in the expected
+   * events being published to the event stream.
+   * 
+   * @throws ChampMarshallingException
+   * @throws ChampSchemaViolationException
+   * @throws ChampObjectNotExistsException
+   * @throws InterruptedException
+   * @throws JsonParseException
+   * @throws JsonMappingException
+   * @throws IOException
+   * @throws ChampUnmarshallingException
+   * @throws ChampRelationshipNotExistsException
+   * @throws ChampIndexNotExistsException
+   */
+  @Test
+  public void indexOperationsTest() throws ChampMarshallingException, 
+                                           ChampSchemaViolationException, 
+                                           ChampObjectNotExistsException, 
+                                           InterruptedException, 
+                                           JsonParseException, 
+                                           JsonMappingException, 
+                                           IOException, 
+                                           ChampUnmarshallingException, 
+                                           ChampRelationshipNotExistsException, 
+                                           ChampIndexNotExistsException {
+        
+    // Create an index object and store it in the graph.
+    ChampObjectIndex objIndex = ChampObjectIndex.create()
+        .ofName("myIndex")
+        .onType("type")
+        .forField("myField")
+        .build();
+    testGraph.storeObjectIndex(objIndex);
+    
+    // Retrieve the next event from the event stream and validate that it is what we expect.
+    String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    assertTrue("Expected STORE event.", loggedEventStr.contains("STORE"));
+    assertTrue("Entity type for store event was not a vertex index.", loggedEventStr.contains("objectIndex"));
+    
+    // Now, delete our partition.
+    testGraph.deleteObjectIndex("myIndex");
+    
+    // Retrieve the next event from the event stream and validate that it is what we expect.
+    loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    assertTrue("Expected DELETE event.", loggedEventStr.contains("DELETE"));
+    assertTrue("Entity type for store event was not a vertex index.", loggedEventStr.contains("objectIndex"));
+  }
+  
+  /**
+   * This test validates that performing index operations in the case where the data to be
+   * forwarded to the event stream is unavailable results in no event being generated, but
+   * does not otherwise create issues.
+   * 
+   * @throws ChampMarshallingException
+   * @throws ChampSchemaViolationException
+   * @throws ChampObjectNotExistsException
+   * @throws InterruptedException
+   * @throws JsonParseException
+   * @throws JsonMappingException
+   * @throws IOException
+   * @throws ChampUnmarshallingException
+   * @throws ChampRelationshipNotExistsException
+   * @throws ChampIndexNotExistsException
+   */
+  @Test
+  public void indexOperationsWithNullsTest() throws ChampMarshallingException, 
+                                                    ChampSchemaViolationException, 
+                                                    ChampObjectNotExistsException, 
+                                                    InterruptedException, 
+                                                    JsonParseException, 
+                                                    JsonMappingException, 
+                                                    IOException, 
+                                                    ChampUnmarshallingException, 
+                                                    ChampRelationshipNotExistsException, 
+                                                    ChampIndexNotExistsException {
+    
+    // Set up our graph to simulate a failure to retrieve some of the data we need to generate
+    // events.
+    testGraph.returnNulls();
+    
+    // Create an index object and store it in the graph.
+    ChampObjectIndex objIndex = ChampObjectIndex.create()
+        .ofName("myIndex")
+        .onType("type")
+        .forField("myField")
+        .build();
+    testGraph.storeObjectIndex(objIndex);
+    
+    // Check our simulated event stream to see if an  an event log was produced.
+    String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    
+    // Now, delete our index.
+    testGraph.deleteObjectIndex("myIndex");
+    
+    // Check our simulated event stream to see if an an event log was produced.
+    loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    
+    // Validate that we did not get an event from the stream.
+    assertNull("Delete partition event should not have been logged to the event stream", loggedEventStr);
+  }
+  
+  
+  /**
+   * This test validates that performing relationship index operations in the case where 
+   * the data to be forwarded to the event stream is unavailable results in no event being 
+   * generated, but does not otherwise create issues.
+   * 
+   * @throws ChampMarshallingException
+   * @throws ChampSchemaViolationException
+   * @throws ChampObjectNotExistsException
+   * @throws InterruptedException
+   * @throws JsonParseException
+   * @throws JsonMappingException
+   * @throws IOException
+   * @throws ChampUnmarshallingException
+   * @throws ChampRelationshipNotExistsException
+   * @throws ChampIndexNotExistsException
+   */
+  @Test
+  public void relationshipIndexOperationsTest() throws ChampMarshallingException, 
+                                                       ChampSchemaViolationException, 
+                                                       ChampObjectNotExistsException, 
+                                                       InterruptedException, 
+                                                       JsonParseException, 
+                                                       JsonMappingException, 
+                                                       IOException, 
+                                                       ChampUnmarshallingException, 
+                                                       ChampRelationshipNotExistsException, 
+                                                       ChampIndexNotExistsException {
+        
+    // Create a relationship index object and store it in the graph.
+    ChampRelationshipIndex relIndex = ChampRelationshipIndex.create()
+        .ofName("myIndex")
+        .onType("type")
+        .forField("myField")
+        .build();
+    testGraph.storeRelationshipIndex(relIndex);
+    
+    // Retrieve the next event from the event stream and validate that it is what we expect.
+    String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    assertTrue("Expected STORE event.", loggedEventStr.contains("STORE"));
+    assertTrue("Entity type for store event was not a relationship index.", loggedEventStr.contains("relationshipIndex"));
+    
+    // Now, delete our partition.
+    testGraph.deleteRelationshipIndex("myIndex");
+    
+    // Retrieve the next event from the event stream and validate that it is what we expect.
+    loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    assertTrue("Expected DELETE event.", loggedEventStr.contains("DELETE"));
+    assertTrue("Entity type for store event was not a relationship index.", loggedEventStr.contains("relationshipIndex"));
+  }
+  
+  
+  /**
+   * This test validates that performing index operations in the case where the data to be
+   * forwarded to the event stream is unavailable results in no event being generated, but
+   * does not otherwise create issues.
+   * 
+   * @throws ChampMarshallingException
+   * @throws ChampSchemaViolationException
+   * @throws ChampObjectNotExistsException
+   * @throws InterruptedException
+   * @throws JsonParseException
+   * @throws JsonMappingException
+   * @throws IOException
+   * @throws ChampUnmarshallingException
+   * @throws ChampRelationshipNotExistsException
+   * @throws ChampIndexNotExistsException
+   */
+  @Test
+  public void relationshipIndexOperationsWithNullsTest() throws ChampMarshallingException, 
+                                                                ChampSchemaViolationException, 
+                                                                ChampObjectNotExistsException, 
+                                                                InterruptedException, 
+                                                                JsonParseException, 
+                                                                JsonMappingException, 
+                                                                IOException, 
+                                                                ChampUnmarshallingException, 
+                                                                ChampRelationshipNotExistsException, 
+                                                                ChampIndexNotExistsException {
+    
+    // Set up our graph to simulate a failure to retrieve some of the data we need to generate
+    // events.
+    testGraph.returnNulls();
+    
+    // Create a relationship index object and store it in the graph.
+    ChampRelationshipIndex relIndex = ChampRelationshipIndex.create()
+        .ofName("myIndex")
+        .onType("type")
+        .forField("myField")
+        .build();
+    
+    testGraph.storeRelationshipIndex(relIndex);
+    
+    // Check our simulated event stream to see if an an event log was produced.
+    String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    
+    // Now, delete our index.
+    testGraph.deleteRelationshipIndex("myIndex");
+    
+    // Check our simulated event stream to see if an event log was produced.
+    loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+    
+    // Validate that we did not get an event from the stream.
+    assertNull("Delete partition event should not have been logged to the event stream", loggedEventStr);
+  }
+      
+  
+  /**
+   * This is a simple graph stub that extends our {@link AbstractLoggingChampGraph} class which 
+   * we can use to validate that log events get generated without worrying about having a real
+   * underlying graph.
+   */
+  private class TestGraph extends AbstractLoggingChampGraph {
+    
+    /** If set, this causes simulated retrieve operations to fail. */
+    private boolean returnNulls = false;
+    
+    
+    protected TestGraph(Map<String, Object> properties, CambriaPublisher producer) {
+      super(properties);
+      
+      setProducer(producer);
+    }
+
+    public void returnNulls() {
+      returnNulls = true;
+    }
+    
+    @Override 
+    public void shutdown() {
+      if(returnNulls) {
+        publisherPool = null;
+      }
+      super.shutdown();
+    }
+    
+    @Override
+    public ChampObject executeStoreObject(ChampObject object) throws ChampMarshallingException,
+                                                                     ChampSchemaViolationException, 
+                                                                     ChampObjectNotExistsException {
+      if(!returnNulls) {
+        return object;
+      } else {
+        return null;
+      }
+    }
+
+    @Override
+    public ChampObject executeReplaceObject(ChampObject object) throws ChampMarshallingException,
+                                                                       ChampSchemaViolationException, 
+                                                                       ChampObjectNotExistsException {
+      if(!returnNulls) {
+        return object;
+      } else {
+        return null;
+      }
+    }
+
+    @Override
+    public Optional<ChampObject> retrieveObject(Object key) throws ChampUnmarshallingException {
+      
+      if(!returnNulls) {
+        return(Optional.of(ChampObject.create()
+                            .ofType("foo")
+                            .withKey(key)
+                            .build()));  
+      } else {
+        return Optional.empty();
+      }
+    }
+
+    @Override
+    public void executeDeleteObject(Object key) throws ChampObjectNotExistsException {
+   
+    }
+
+    @Override
+    public Stream<ChampObject> queryObjects(Map<String, Object> queryParams) {
+      // Not used by any tests.
+      return null;
+    }
+
+    @Override
+    public ChampRelationship executeStoreRelationship(ChampRelationship relationship) 
+        throws ChampUnmarshallingException, 
+               ChampMarshallingException, 
+               ChampObjectNotExistsException, 
+               ChampSchemaViolationException,
+               ChampRelationshipNotExistsException {
+
+      if(!returnNulls) {
+        return relationship;
+      } else {
+        return null;
+      }
+    }
+
+    @Override
+    public ChampRelationship executeReplaceRelationship(ChampRelationship relationship)
+        throws ChampUnmarshallingException, 
+               ChampMarshallingException,
+               ChampSchemaViolationException, 
+               ChampRelationshipNotExistsException {
+
+      if(!returnNulls) {
+        return relationship;
+      } else {
+        return null;
+      }
+    }
+
+    @Override
+    public Optional<ChampRelationship> retrieveRelationship(Object key) throws ChampUnmarshallingException {
+      // Not used by any tests.
+      return null;
+    }
+
+    @Override
+    public void executeDeleteRelationship(ChampRelationship relationship) throws ChampRelationshipNotExistsException {
+      // Not used by any tests.   
+    }
+
+    @Override
+    public Stream<ChampRelationship> retrieveRelationships(ChampObject object)
+        throws ChampUnmarshallingException, ChampObjectNotExistsException {
+      
+      // Not used by any tests.
+      return null;
+    }
+
+    @Override
+    public Stream<ChampRelationship> queryRelationships(Map<String, Object> queryParams) {
+      
+      // Not used by any tests.
+      return null;
+    }
+
+    @Override
+    public ChampPartition executeStorePartition(ChampPartition partition) 
+        throws ChampSchemaViolationException, 
+               ChampRelationshipNotExistsException,
+               ChampMarshallingException, 
+               ChampObjectNotExistsException {
+
+      if(!returnNulls) {
+        return partition;
+      } else {
+        return null;
+      }
+    }
+
+    @Override
+    public void executeDeletePartition(ChampPartition graph) {
+      // Not used by any tests.     
+    }
+
+    @Override
+    public void executeStoreObjectIndex(ChampObjectIndex index) {
+      // Not used by any tests.    
+    }
+
+    @Override
+    public Optional<ChampObjectIndex> retrieveObjectIndex(String indexName) {
+      
+      if(!returnNulls) {
+        return Optional.of(ChampObjectIndex.create()
+                            .ofName(indexName)
+                            .onType("doesnt matter")
+                            .forField("doesnt matter")
+                            .build());
+      } else {
+        return Optional.empty();
+      }
+    }
+
+    @Override
+    public Stream<ChampObjectIndex> retrieveObjectIndices() {
+      // Not used by any tests.
+      return null;
+    }
+
+    @Override
+    public void executeDeleteObjectIndex(String indexName) throws ChampIndexNotExistsException {
+      // Not used by any tests.    
+    }
+
+    @Override
+    public void executeStoreRelationshipIndex(ChampRelationshipIndex index) {
+      // Not used by any tests.  
+    }
+
+    @Override
+    public Optional<ChampRelationshipIndex> retrieveRelationshipIndex(String indexName) {
+      if(!returnNulls) {
+        return Optional.of(ChampRelationshipIndex.create()
+                            .ofName(indexName)
+                            .onType("doesnt matter")
+                            .forField("doesnt matter")
+                            .build());
+      } else {
+        return Optional.empty();
+      }
+    }
+
+    @Override
+    public Stream<ChampRelationshipIndex> retrieveRelationshipIndices() {
+      // Not used by any tests.
+      return null;
+    }
+
+    @Override
+    public void executeDeleteRelationshipIndex(String indexName)
+        throws ChampIndexNotExistsException {
+      // Not used by any tests.    
+    }
+
+    @Override
+    public void storeSchema(ChampSchema schema) throws ChampSchemaViolationException {
+      // Not used by any tests.    
+    }
+
+    @Override
+    public ChampSchema retrieveSchema() {
+      // Not used by any tests.
+      return null;
+    }
+
+    @Override
+    public void updateSchema(ChampObjectConstraint objectConstraint)
+        throws ChampSchemaViolationException {
+      // Not used by any tests.    
+    }
+
+    @Override
+    public void updateSchema(ChampRelationshipConstraint schema)
+        throws ChampSchemaViolationException {
+      // Not used by any tests.     
+    }
+
+    @Override
+    public void deleteSchema() {
+      // Not used by any tests.  
+    }
+
+    @Override
+    public ChampCapabilities capabilities() {
+      // Not used by any tests.
+      return null;
+    }
+  }
+  
+  private class InMemoryPublisher implements CambriaPublisher {
+
+    public BlockingQueue<String> eventStream = new ArrayBlockingQueue<String>(50);
+    public BlockingQueue<String> failedMsgs = new ArrayBlockingQueue<String>(10);
+    
+    private boolean failMode=false;
+    
+    public void enterFailMode() {
+      failMode=true;
+    }
+    
+    @Override
+    public void logTo(Logger log) {
+      // Not used by any tests. 
+    }
+
+    @Override
+    public void setApiCredentials(String apiKey, String apiSecret) {
+      // Not used by any tests.  
+    }
+
+    @Override
+    public void clearApiCredentials() {
+      // Not used by any tests.  
+    }
+
+    @Override
+    public void setHttpBasicCredentials(String username, String password) {
+      // Not used by any tests.  
+    }
+
+    @Override
+    public void clearHttpBasicCredentials() {
+      // Not used by any tests.  
+    }
+
+    @Override
+    public int send(String partition, String msg) throws IOException {
+      
+      if(!failMode) {
+        eventStream.add(msg);
+        return 0;
+      } else {
+        failedMsgs.add(msg);
+        throw new IOException("nope");
+      }
+    }
+
+    @Override
+    public int send(message msg) throws IOException {
+      eventStream.add(msg.toString());
+      return 0;
+    }
+
+    @Override
+    public int send(Collection<message> msgs) throws IOException {
+      for(message msg : msgs) {
+        eventStream.add(msg.toString());
+      }
+      return 0;
+    }
+
+    @Override
+    public void close() {
+      // Not used by any tests.
+    }     
+  }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/exceptions/ChampExceptionTest.java b/src/test/java/org/openecomp/aai/champ/exceptions/ChampExceptionTest.java
new file mode 100644 (file)
index 0000000..3f813f5
--- /dev/null
@@ -0,0 +1,150 @@
+/**
+ * ============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.openecomp.aai.champ.exceptions;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class ChampExceptionTest {
+
+       @Test
+       public void testChampIndexNotExistsException() {
+               final ChampIndexNotExistsException e1 = new ChampIndexNotExistsException();
+
+               assertTrue(e1.getMessage() == null);
+
+               final ChampIndexNotExistsException e2 = new ChampIndexNotExistsException("foo");
+
+               assertTrue(e2.getMessage().equals("foo"));
+
+               final ChampIndexNotExistsException e3 = new ChampIndexNotExistsException(e2);
+
+               assertTrue(e3.getCause().equals(e2));
+
+               final ChampIndexNotExistsException e4 = new ChampIndexNotExistsException("foo", e3);
+
+               assertTrue(e4.getMessage().equals("foo"));
+               assertTrue(e4.getCause().equals(e3));
+       }
+
+       @Test
+       public void testChampMarshallingException() {
+               final ChampMarshallingException e1 = new ChampMarshallingException();
+
+               assertTrue(e1.getMessage() == null);
+
+               final ChampMarshallingException e2 = new ChampMarshallingException("foo");
+
+               assertTrue(e2.getMessage().equals("foo"));
+
+               final ChampIndexNotExistsException e3 = new ChampIndexNotExistsException(e2);
+
+               assertTrue(e3.getCause().equals(e2));
+
+               final ChampMarshallingException e4 = new ChampMarshallingException("foo", e3);
+
+               assertTrue(e4.getMessage().equals("foo"));
+               assertTrue(e4.getCause().equals(e3));
+       }
+
+       @Test
+       public void testChampObjectNotExistsException() {
+               final ChampObjectNotExistsException e1 = new ChampObjectNotExistsException();
+
+               assertTrue(e1.getMessage() == null);
+
+               final ChampObjectNotExistsException e2 = new ChampObjectNotExistsException("foo");
+
+               assertTrue(e2.getMessage().equals("foo"));
+
+               final ChampIndexNotExistsException e3 = new ChampIndexNotExistsException(e2);
+
+               assertTrue(e3.getCause().equals(e2));
+
+               final ChampObjectNotExistsException e4 = new ChampObjectNotExistsException("foo", e3);
+
+               assertTrue(e4.getMessage().equals("foo"));
+               assertTrue(e4.getCause().equals(e3));
+       }
+
+       @Test
+       public void testChampRelationshipNotExistsException() {
+               final ChampRelationshipNotExistsException e1 = new ChampRelationshipNotExistsException();
+
+               assertTrue(e1.getMessage() == null);
+
+               final ChampRelationshipNotExistsException e2 = new ChampRelationshipNotExistsException("foo");
+
+               assertTrue(e2.getMessage().equals("foo"));
+
+               final ChampIndexNotExistsException e3 = new ChampIndexNotExistsException(e2);
+
+               assertTrue(e3.getCause().equals(e2));
+
+               final ChampRelationshipNotExistsException e4 = new ChampRelationshipNotExistsException("foo", e3);
+
+               assertTrue(e4.getMessage().equals("foo"));
+               assertTrue(e4.getCause().equals(e3));
+       }
+
+       @Test
+       public void testChampSchemaViolationException() {
+               final ChampSchemaViolationException e1 = new ChampSchemaViolationException();
+
+               assertTrue(e1.getMessage() == null);
+
+               final ChampSchemaViolationException e2 = new ChampSchemaViolationException("foo");
+
+               assertTrue(e2.getMessage().equals("foo"));
+
+               final ChampIndexNotExistsException e3 = new ChampIndexNotExistsException(e2);
+
+               assertTrue(e3.getCause().equals(e2));
+
+               final ChampSchemaViolationException e4 = new ChampSchemaViolationException("foo", e3);
+
+               assertTrue(e4.getMessage().equals("foo"));
+               assertTrue(e4.getCause().equals(e3));
+       }
+
+       @Test
+       public void testChampUnmarshallingException() {
+               final ChampUnmarshallingException e1 = new ChampUnmarshallingException();
+
+               assertTrue(e1.getMessage() == null);
+
+               final ChampUnmarshallingException e2 = new ChampUnmarshallingException("foo");
+
+               assertTrue(e2.getMessage().equals("foo"));
+
+               final ChampIndexNotExistsException e3 = new ChampIndexNotExistsException(e2);
+
+               assertTrue(e3.getCause().equals(e2));
+
+               final ChampUnmarshallingException e4 = new ChampUnmarshallingException("foo", e3);
+
+               assertTrue(e4.getMessage().equals("foo"));
+               assertTrue(e4.getCause().equals(e3));
+       }
+
+}
diff --git a/src/test/java/org/openecomp/aai/champ/ie/ExportTest.java b/src/test/java/org/openecomp/aai/champ/ie/ExportTest.java
new file mode 100644 (file)
index 0000000..7ecb3de
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * ============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.openecomp.aai.champ.ie;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+
+public class ExportTest {
+
+       @Test
+       public void testGraphMLExport() {
+
+               final GraphMLImporterExporter ie = new GraphMLImporterExporter();
+               final ChampAPI api = ChampAPI.Factory.newInstance(ChampGraph.Type.IN_MEMORY);
+
+               ie.importData(api, getClass().getClassLoader().getResourceAsStream("import-test.graphml"));
+
+               ie.exportData(api.getGraph("unit-test"), System.out);
+
+               api.shutdown();
+       }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/ie/ImportTest.java b/src/test/java/org/openecomp/aai/champ/ie/ImportTest.java
new file mode 100644 (file)
index 0000000..ba2cbb1
--- /dev/null
@@ -0,0 +1,93 @@
+/**
+ * ============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.openecomp.aai.champ.ie;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Optional;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+
+public class ImportTest {
+
+       private final String GRAPH_NAME = "unit-test";
+
+       @Test
+       public void testGraphMLImport() {
+
+               final GraphMLImporterExporter importer = new GraphMLImporterExporter();
+               final ChampAPI api = ChampAPI.Factory.newInstance(ChampGraph.Type.IN_MEMORY);
+
+               importer.importData(api, getClass().getClassLoader().getResourceAsStream("import-test.graphml"));
+
+               final ChampGraph graph = api.getGraph(GRAPH_NAME);
+
+               graph.queryObjects(Collections.emptyMap()).forEach(object -> {
+                       final Optional<String> nameOpt = object.getProperty("name");
+                       final Optional<Boolean> studentOpt = object.getProperty("student");
+                       final Optional<Long> worthOpt = object.getProperty("worth");
+                       final Optional<Integer> ageOpt = object.getProperty("age");
+                       final Optional<Float> heightOpt = object.getProperty("height");
+                       final Optional<Double> weightOpt = object.getProperty("weight");
+                       final Optional<String> favoriteColorOpt = object.getProperty("favoriteColor");
+
+                       final String name = nameOpt.get();
+
+                       if (name.equals("Champ")) {
+                               assertTrue(!studentOpt.isPresent());
+                               assertTrue(!ageOpt.isPresent());
+                               assertTrue(!worthOpt.isPresent());
+                               assertTrue(!heightOpt.isPresent());
+                               assertTrue(!weightOpt.isPresent());
+                               assertTrue(favoriteColorOpt.get().equals("green"));
+                       } else if (name.equals("Max")) {
+                               assertTrue(!studentOpt.isPresent());
+                               assertTrue(!ageOpt.isPresent());
+                               assertTrue(!worthOpt.isPresent());
+                               assertTrue(!heightOpt.isPresent());
+                               assertTrue(!weightOpt.isPresent());
+                               assertTrue(favoriteColorOpt.get().equals("red"));
+                       } else if (name.equals("Ace")) {
+                               assertTrue(studentOpt.get());
+                               assertTrue(worthOpt.get().equals(50000L));
+                               assertTrue(ageOpt.get().equals(21));
+                               assertTrue(heightOpt.get().equals(72.5f));
+                               assertTrue(weightOpt.get().equals(180.5d));
+                               assertTrue(favoriteColorOpt.get().equals("yellow"));
+                       } else if (name.equals("Fido")) {
+                               assertTrue(!studentOpt.isPresent());
+                               assertTrue(!ageOpt.isPresent());
+                               assertTrue(!worthOpt.isPresent());
+                               assertTrue(!heightOpt.isPresent());
+                               assertTrue(!weightOpt.isPresent());
+                               assertTrue(favoriteColorOpt.get().equals("blue"));
+                       } else {
+                               throw new AssertionError("Unknown object " + name + " - update unit test");
+                       }
+               });
+
+               api.shutdown();
+       }
+}
diff --git a/src/test/resources/import-test.graphml b/src/test/resources/import-test.graphml
new file mode 100755 (executable)
index 0000000..fd13b6b
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
+       <key id="d0" for="node" attr.name="student" attr.type="boolean"/>
+       <key id="d1" for="node" attr.name="worth" attr.type="long"/>
+       <key id="d2" for="node" attr.name="age" attr.type="int"/>
+       <key id="d3" for="node" attr.name="height" attr.type="float"/>
+       <key id="d4" for="node" attr.name="weight" attr.type="double"/>
+       <key id="d5" for="node" attr.name="favoriteColor" attr.type="string">
+               <default>green</default>
+       </key>
+       <key id="d6" for="node" attr.name="name" attr.type="string"/>
+       <key id="d7" for="edge" attr.name="at" attr.type="long"/>
+       <key id="d8" for="node" attr.name="type" attr.type="string"/>
+       <key id="d9" for="edge" attr.name="type" attr.type="string"/>
+       <graph id="unit-test" edgedefault="directed">
+               <node id="n0">
+                       <data key="d0">true</data>
+                       <data key="d1">50000</data>
+                       <data key="d2">21</data>
+                       <data key="d3">72.5</data>
+                       <data key="d4">180.5</data>
+                       <data key="d5">yellow</data>
+                       <data key="d6">Ace</data>
+                       <data key="d8">Dog</data>
+               </node>
+               <node id="n1">
+                       <data key="d6">Champ</data>
+                       <data key="d8">Dog</data>
+               </node>
+               <node id="n2">
+                       <data key="d5">blue</data>
+                       <data key="d6">Fido</data>
+                       <data key="d8">Dog</data>
+               </node>
+               <node id="n3">
+                       <data key="d5">red</data>
+                       <data key="d6">Max</data>
+                       <data key="d8">Dog</data>
+               </node>
+               <edge id="e0" source="n0" target="n2">
+                       <data key="d7">12348234</data>
+                       <data key="d9">Dog</data>
+               </edge>
+               <edge id="e3" source="n3" target="n2">
+                       <data key="d9">Dog</data>
+               </edge>
+       </graph>
+</graphml>
\ No newline at end of file
diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml
new file mode 100644 (file)
index 0000000..72cd644
--- /dev/null
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    ============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.
+
+-->
+<configuration>
+
+       <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+               <encoder>
+                   <pattern>%d %-5level [%thread] %logger{255}: %msg%n</pattern>
+               </encoder>
+       </appender>
+
+       <logger name="com.thinkaurelius.titan" level="OFF"/>
+       <logger name="com.netflix.astyanax" level="OFF"/>
+       <logger name="com.datastax" level="ERROR"/>
+       <logger name="org.apache.hadoop" level="ERROR"/>
+       <logger name="org.apache.zookeeper" level="ERROR"/>
+
+       <root level="INFO">
+               <appender-ref ref="STDOUT" />
+       </root>
+
+</configuration>