[AAI-26] Adding gizmo data to the repository. 71/6571/2
authorShwetank Dave <shwetank.dave@amdocs.com>
Wed, 2 Aug 2017 21:10:38 +0000 (17:10 -0400)
committerShwetank Dave <shwetank.dave@amdocs.com>
Thu, 3 Aug 2017 15:28:39 +0000 (11:28 -0400)
Change-Id: I183f837d45acbfe3c673fde1acf8768d5e3fd37b
Signed-off-by: Shwetank Dave <shwetank.dave@amdocs.com>
82 files changed:
.gitignore [new file with mode: 0644]
.gitreview [new file with mode: 0644]
License.txt [new file with mode: 0644]
README.md [new file with mode: 0644]
ajsc-shared-config/README.txt [new file with mode: 0644]
ajsc-shared-config/etc/aft.properties [new file with mode: 0644]
ajsc-shared-config/etc/basic-logback_root_logger_level_off.xml [new file with mode: 0644]
ajsc-shared-config/etc/logback.xml [new file with mode: 0644]
ajsc-shared-config/etc/spm2.jks [new file with mode: 0644]
antBuild/build.xml [new file with mode: 0644]
bundleconfig-local/README.txt [new file with mode: 0644]
bundleconfig-local/RELEASE_NOTES.txt [new file with mode: 0644]
bundleconfig-local/etc/appprops/AAFUserRoles.properties [new file with mode: 0644]
bundleconfig-local/etc/appprops/PostProcessorInterceptors.properties [new file with mode: 0644]
bundleconfig-local/etc/appprops/PreProcessorInterceptors.properties [new file with mode: 0644]
bundleconfig-local/etc/appprops/app-intercepts.properties [new file with mode: 0644]
bundleconfig-local/etc/appprops/methodMapper.properties [new file with mode: 0644]
bundleconfig-local/etc/sysprops/sys-props.properties [new file with mode: 0644]
pom.xml [new file with mode: 0644]
src/main/ajsc/crud-api_v1/crud-api/v1/conf/jaxrsBeans.groovy [new file with mode: 0644]
src/main/ajsc/crud-api_v1/crud-api/v1/docs/README.txt [new file with mode: 0644]
src/main/ajsc/crud-api_v1/crud-api/v1/lib/README.txt [new file with mode: 0644]
src/main/ajsc/crud-api_v1/crud-api/v1/props/module.props [new file with mode: 0644]
src/main/ajsc/crud-api_v1/crud-api/v1/routes/crud.route [new file with mode: 0644]
src/main/ajsc/crud-api_v1/crud-api/v1/routes/helloWorld.route [new file with mode: 0644]
src/main/ajsc/crud-api_v1/crud-api/v1/routes/jaxrsExample.route [new file with mode: 0644]
src/main/assemble/ajsc_module_assembly.xml [new file with mode: 0644]
src/main/assemble/ajsc_props_assembly.xml [new file with mode: 0644]
src/main/assemble/ajsc_runtime_assembly.xml [new file with mode: 0644]
src/main/bin/start.sh [new file with mode: 0644]
src/main/config/ajsc-chef.jks [new file with mode: 0644]
src/main/config/ajsc-jetty.xml [new file with mode: 0644]
src/main/config/ajsc-override-web.xml [new file with mode: 0644]
src/main/config/ajscJetty.jks [new file with mode: 0644]
src/main/config/cadi.properties [new file with mode: 0644]
src/main/config/jul-redirect.properties [new file with mode: 0644]
src/main/config/keyfile [new file with mode: 0644]
src/main/config/runner-web.xml [new file with mode: 0644]
src/main/docker/Dockerfile [new file with mode: 0644]
src/main/java/org/openecomp/crud/dao/GraphDao.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/dao/champ/ChampDao.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/entity/Edge.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/entity/Vertex.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/event/GraphEvent.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/event/GraphEventEdge.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/event/GraphEventVertex.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/exception/CrudException.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/logging/CrudServiceMsgs.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/logging/LoggingUtil.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/parser/CrudResponseBuilder.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/service/CrudGraphDataService.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/service/CrudRestService.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/service/EdgePayload.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/service/JaxrsEchoService.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/service/VertexPayload.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/util/CrudJaxbTransformation.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/util/CrudProperties.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/util/CrudServiceConstants.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/util/CrudServiceUtil.java [new file with mode: 0644]
src/main/java/org/openecomp/crud/util/FileWatcher.java [new file with mode: 0644]
src/main/java/org/openecomp/schema/OxmModelLoader.java [new file with mode: 0644]
src/main/java/org/openecomp/schema/OxmModelValidator.java [new file with mode: 0644]
src/main/java/org/openecomp/schema/RelationshipSchema.java [new file with mode: 0644]
src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java [new file with mode: 0644]
src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java [new file with mode: 0644]
src/main/resources/logging/CrudServiceMsgs.properties [new file with mode: 0644]
src/main/runtime/context/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.context [new file with mode: 0644]
src/main/runtime/context/default#0.context [new file with mode: 0644]
src/main/runtime/deploymentPackage/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.json [new file with mode: 0644]
src/main/runtime/shiroRole/ajscadmin.json [new file with mode: 0644]
src/main/runtime/shiroRole/contextadmin#__module.ajsc.namespace.name__.json [new file with mode: 0644]
src/main/runtime/shiroRole/contextadmin#default.json [new file with mode: 0644]
src/main/runtime/shiroUser/ajsc.json [new file with mode: 0644]
src/main/runtime/shiroUserRole/ajsc#ajscadmin.json [new file with mode: 0644]
src/main/runtime/shiroUserRole/ajsc#contextadmin#__module.ajsc.namespace.name__.json [new file with mode: 0644]
src/main/runtime/shiroUserRole/ajsc#contextadmin#default.json [new file with mode: 0644]
src/test/java/org/openecomp/crud/dao/champ/ChampDaoTest.java [new file with mode: 0644]
src/test/java/org/openecomp/schema/OxmModelLoaderTest.java [new file with mode: 0644]
src/test/resources/gremlin/gremlinResponseEmptyVertex.json [new file with mode: 0644]
src/test/resources/gremlin/gremlinResponseMultipleVertex.json [new file with mode: 0644]
src/test/resources/gremlin/gremlinResponseVertex.json [new file with mode: 0644]
version.properties [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..5c01c1a
--- /dev/null
@@ -0,0 +1,7 @@
+.classpath
+.project
+.settings/
+target/
+logs/
+debug-logs/
+.idea/
\ No newline at end of file
diff --git a/.gitreview b/.gitreview
new file mode 100644 (file)
index 0000000..1a6d347
--- /dev/null
@@ -0,0 +1,4 @@
+[gerrit]
+host=gerrit.onap.org
+port=29418
+project=aai/gizmo.git
diff --git a/License.txt b/License.txt
new file mode 100644 (file)
index 0000000..810e147
--- /dev/null
@@ -0,0 +1,21 @@
+============LICENSE_START=======================================================
+Gizmo
+================================================================================
+Copyright © 2017 AT&T Intellectual Property.
+Copyright © 2017 Amdocs
+All rights reserved.
+================================================================================
+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..adb8bdb
--- /dev/null
+++ b/README.md
@@ -0,0 +1,636 @@
+runsOnPserverrunsOnPserver# CRUD Microservice (Gizmo)
+
+## Overview
+The CRUD microservice implements a set of RESTful APIs which allow a client to perform CREATE, UPDATE, GET, and DELETE operations on verticies and edges within the A&AI graph database.
+
+## Getting Started
+
+### Building The Microservice
+
+After cloning the project, execute the following Maven command from the project's top level directory to build the project:
+
+    > mvn clean install
+
+Now, you can build your Docker image:
+
+    > docker build -t openecomp/crud-service target 
+    
+### Deploying The Microservice 
+
+Push the Docker image that you have built to your Docker repository and pull it down to the location from which you will be running the service.
+
+**Create the following directories on the host machine:**
+
+    ../logs
+    ../appconfig
+       ../appconfig/auth
+    
+You will be mounting these as data volumes when you start the Docker container.
+
+#### Configuring the Microservice
+
+Create configuration file **../appconfig/crud-api.properties**
+
+       # List of hostnames/addresses of the graph database
+       crud.graph.host=graphhost1.onap.com,graphhost2.onap.com
+       
+       # Port on which to connect to the graph database
+       crud.graph.port=2181
+       
+       # Name of the graph on which this service will operate
+       crud.graph.name=aaigraphautomation
+       
+       # Backend storage type for the graph.  Types currently supported:
+       #  - cassandra
+       #  - hbase
+       crud.storage.backend.db=cassandra
+       
+       # List of hostnames/addresses of the DMaaP/Kafka cluster on which to post notification events
+       event.stream.hosts=kafkahost1.onap.com,kafkahost2.onap.com
+       
+       # Number of events to bath up before posting to DMaaP/Kafka
+       event.stream.batch-size=100
+       
+       # Amount of time (in ms) to wait before sending batch of events (when batch does not reach batch-size)
+       event.stream.batch-timeout=60000
+
+Create configuration file **../appconfig/auth/crud-policy.json**
+
+This policy file defines which client certificates are authorized to use the service's APIs.  An example policy file follows:
+
+    {
+        "roles": [
+            {
+                "name": "admin",
+                "functions": [
+                    {
+                        "name": "search", "methods": [ { "name": "GET" },{ "name": "DELETE" }, { "name": "PUT" }, { "name": "POST" } ]
+                    }
+                ],
+                "users": [
+                    {
+                        "username": "CN=admin, OU=My Organization Unit, O=, L=Sometown, ST=SomeProvince, C=CA"
+                    }    
+                ]
+            }
+        ]
+    }
+
+Create keystore file **../appconfig/auth/tomcat\_keystore**
+_tomcat\_keystore_
+
+Create a keystore with this name containing whatever CA certificates that you want your instance of the CRUD service to accept for HTTPS traffic.
+
+#### Start the service
+
+You can now start the Docker container in the following manner:
+
+       docker run -d \
+           -p 9520:9520 \
+               -e CONFIG_HOME=/opt/app/crud-service/config/ \
+               -e KEY_STORE_PASSWORD={{obfuscated password}} \
+               -e KEY_MANAGER_PASSWORD=OBF:{{obfuscated password}} \
+           -v /<path>/logs:/opt/aai/logroot/AAI-CRUD \
+           -v /<path>/appconfig:/opt/app/crud-service/config \
+           --name crud-service \
+           {{your docker repo}}/crud-service
+    
+Where,
+
+    {{your docker repo}} = The Docker repository you have published your CRUD Service image to.
+    {{obfuscated password}} = The password for your key store/key manager after running it through the Jetty obfuscation tool.
+
+## API Definitions
+
+### Echo API
+
+       URL: https://<host>:9520/services/crud-api/v1/echo-service/echo/<input>
+       Method: GET
+       Success Response: 200
+       
+### Create Vertex
+
+Vertex payload data is validated against oxm.
+* Mandatory attributes are required in payload
+* Data type validation is enforced
+* Defaults from oxm schema used when not specified in payload
+
+       URL: https://<host>:9520/services/inventory/v8/pserver/
+       Method: POST
+       Body:
+               {   
+                       "properties":{   
+                               "ptnii-equip-name":"e-name",
+                               "equip-type":"server",
+                               "hostname":"myhost",
+                               "equip-vendor":"HP",
+                               "equip-model":"DL380p-nd",
+                               "fqdn":myhost.onap.net",
+                               "purpose":"my-purpose",
+                               "resource-version":"1477013499",
+                               "ipv4-oam-address":"1.2.3.4"
+                       }
+               }
+       Success Response:
+               Code: 201
+               Content:
+                       {   
+                               "id":"1130672",
+                               "type":"pserver",
+                               "url":"services/inventory/v8/pserver/1130672",
+                               "properties":{   
+                                       "ptnii-equip-name":"e-name",
+                                       "equip-type":"server",
+                                       "hostname":"myhost",
+                                       "equip-vendor":"HP",
+                                       "equip-model":"DL380p-nd",
+                                       "fqdn":myhost.onap.net",
+                                       "purpose":"my-purpose",
+                                       "resource-version":"1477013499",
+                                       "ipv4-oam-address":"1.2.3.4"
+                               }
+                       }       
+       Error Response:
+               Code: 400 (BAD REQUEST)
+               Content: Error message describing the bad request failure.
+               Situation: Invalid Payload or schema error.
+
+               Code: 403 (FORBIDDEN)
+               Content: Error message describing the Authorization failure.
+               Situation: Authorization failure.
+
+               Code: 415 (UNSUPPORTED MEDIA TYPE)
+               Situation: Unsupported content type .
+               
+               Code: 500 (Internal Server Error)
+               Content: Error message describing the failure.
+               Situation: Any scenario not covered by the above error codes.
+
+Optionally, a vertex can be created by posting to an endpoint which doesn't include the vertex type.
+
+       URL: https://<host>:9520/services/inventory/v8/
+       Method: POST
+       Body:
+               {   
+                       "type":"pserver",
+                       "properties":{   
+                               "ptnii-equip-name":"e-name",
+                               "equip-type":"server",
+                               "hostname":"myhost",
+                               "equip-vendor":"HP",
+                               "equip-model":"DL380p-nd",
+                               "fqdn":myhost.onap.net",
+                               "purpose":"my-purpose",
+                               "resource-version":"1477013499",
+                               "ipv4-oam-address":"1.2.3.4"
+                       }
+               }
+       Success Response:
+               Code: 201
+               Content:
+                       {   
+                               "id":"1130672",
+                               "type":"pserver",
+                               "url":"services/inventory/v8/pserver/1130672",
+                               "properties":{   
+                                       "ptnii-equip-name":"e-name",
+                                       "equip-type":"server",
+                                       "hostname":"myhost",
+                                       "equip-vendor":"HP",
+                                       "equip-model":"DL380p-nd",
+                                       "fqdn":myhost.onap.net",
+                                       "purpose":"my-purpose",
+                                       "resource-version":"1477013499",
+                                       "ipv4-oam-address":"1.2.3.4"
+                               }
+                       }       
+       Error Response:
+               Code: 400 (BAD REQUEST)
+               Content: Error message describing the bad request failure.
+               Situation: Invalid Payload or schema error.
+
+               Code: 403 (FORBIDDEN)
+               Content: Error message describing the Authorization failure.
+               Situation: Authorization failure.
+
+               Code: 415 (UNSUPPORTED MEDIA TYPE)
+               Situation: Unsupported content type .
+               
+               Code: 500 (Internal Server Error)
+               Content: Error message describing the failure.
+               Situation: Any scenario not covered by the above error codes.
+### Get Vertex
+
+       URL: https://<host>:9520/services/inventory/v8/pserver/<id>
+       Method: GET
+       Success Response:
+               Code: 200
+               Content:
+                       {   
+                               "id":"1130672",
+                               "type":"pserver",
+                               "url":"services/inventory/v8/pserver/<id>",
+                               "properties":{   
+                                       "ptnii-equip-name":"e-name",
+                                       "equip-type":"server",
+                                       "hostname":"myhost",
+                                       "equip-vendor":"HP",
+                                       "equip-model":"DL380p-nd",
+                                       "fqdn":myhost.onap.net",
+                                       "purpose":"my-purpose",
+                                       "resource-version":"1477013499",
+                                       "ipv4-oam-address":"1.2.3.4"
+                               },
+                               "in":[   
+                               ],
+                               "out":[   
+                                       {   
+                                               "id":"1crwnu-6hc-d6vp-oe08g",
+                                               "type":"has",
+                                               "target":"services/inventory/v8/vserver/40964272",
+                                               "url":"services/inventory/relationships/v8/has/1crwnu-6hc-d6vp-oe08g"
+                                       }
+                               ]
+                       }       
+       Error Response:
+               Code: 404 (NOT FOUND)
+               Situation: Reource Not found
+
+               Code: 403 (FORBIDDEN)
+               Content: Error message describing the Authorization failure.
+               Situation: Authorization failure.
+
+               Code: 415 (UNSUPPORTED MEDIA TYPE)
+               Situation: Unsupported content type .
+               
+               Code: 500 (Internal Server Error)
+               Content: Error message describing the failure.
+               Situation: Any scenario not covered by the above error codes.
+
+### Get Vertices
+
+       URL: https://<host>:9520/services/inventory/v8/pserver/
+       Optional Query Param: ?equip-vendor=HP
+       Method: GET
+       Success Response:
+               Code: 200
+               Content:
+                       [   
+                               {   
+                                       "id":"950296",
+                                       "type":"pserver",
+                                       "url":"services/inventory/v8/pserver/950296"
+                               },
+                               {   
+                                       "id":"1126576",
+                                       "type":"pserver",
+                                       "url":"services/inventory/v8/pserver/1126576"
+                               },
+                               {   
+                                       "id":"1032384",
+                                       "type":"pserver",
+                                       "url":"services/inventory/v8/pserver/1032384"
+                               }
+                       ]       
+       Error Response:
+               Code: 404 (NOT FOUND)
+               Situation: Reource Not found
+
+               Code: 403 (FORBIDDEN)
+               Content: Error message describing the Authorization failure.
+               Situation: Authorization failure.
+
+               Code: 415 (UNSUPPORTED MEDIA TYPE)
+               Situation: Unsupported content type .
+               
+               Code: 500 (Internal Server Error)
+               Content: Error message describing the failure.
+               Situation: Any scenario not covered by the above error codes.           
+
+### Update Vertex
+
+The PUT command is used to modify an existing vertex.  By default, the vertex data is replaced by the content of the payload.  However, teh following parameter can be added to the header to perform a PATCH instead of a replace:
+**X-HTTP-Method-Override=Patch**
+
+       URL: https://<host>:9520/services/inventory/v8/pserver/<id>
+       Method: PUT
+       Body: Same as POST      
+       Success Response:
+               Code: 201
+               Content: Same as POST   
+       Error Response:
+               Code: 400 (BAD REQUEST)
+               Content: Error message describing the bad request failure.
+               Situation: Invalid Payload or schema error.
+
+               Code: 403 (FORBIDDEN)
+               Content: Error message describing the Authorization failure.
+               Situation: Authorization failure.
+
+               Code: 415 (UNSUPPORTED MEDIA TYPE)
+               Situation: Unsupported content type .
+               
+               Code: 500 (Internal Server Error)
+               Content: Error message describing the failure.
+               Situation: Any scenario not covered by the above error codes.
+
+### Patch Vertex
+
+       URL: https://<host>:9520/services/inventory/v8/pserver/<id>
+       Method: PATCH (Content-Type header set to application/merge-patch+json)
+       Body:
+               {   
+                       "properties":{   
+                               "ptnii-equip-name":"e-name",
+                               "resource-version":"1477013499",
+                               "ipv4-oam-address":"1.2.3.99"
+                       }
+               }
+       Success Response:
+               Code: 200
+               Content: Same as POST           
+       Error Response:
+               Code: 400 (BAD REQUEST)
+               Content: Error message describing the bad request failure.
+               Situation: Invalid Payload or schema error.
+
+               Code: 403 (FORBIDDEN)
+               Content: Error message describing the Authorization failure.
+               Situation: Authorization failure.
+
+               Code: 415 (UNSUPPORTED MEDIA TYPE)
+               Situation: Unsupported content type .
+               
+               Code: 500 (Internal Server Error)
+               Content: Error message describing the failure.
+               Situation: Any scenario not covered by the above error codes.
+ ### Delete Vertex
+
+       URL: https://<host>:9520/services/inventory/v8/pserver/<id>
+       Method: DELETE
+       Success Response:
+               Code: 200               
+       Error Response:
+               Code: 404 (NOT FOUND)
+               Situation: Resource not found
+
+               Code: 403 (FORBIDDEN)
+               Content: Error message describing the Authorization failure.
+               Situation: Authorization failure.
+
+               Code: 415 (UNSUPPORTED MEDIA TYPE)
+               Situation: Unsupported content type .
+               
+               Code: 500 (Internal Server Error)
+               Content: Error message describing the failure.
+               Situation: Any scenario not covered by the above error codes.
+
+### Create Edge
+
+When creating an edge, the CRUD service will validate:
+* properties match the defined schema
+* relationship is valid between the source and target
+
+       URL: https://<host>:9520/services/inventory/relationships/v8/runsOnPserver/
+       Method: POST
+       Body:
+               {     
+                       "source":"services/inventory/v8/vserver/0",
+                       "target":"services/inventory/v8/pserver/7",
+                       "properties":{   
+                               "multiplicity":"many",
+                               "is-parent":true,
+                               "uses-resource":"true",
+                               "has-del-target":"true"
+                       }
+               }
+       Success Response:
+               Code: 201
+               Content:
+                       {   
+                               "id":"215x5m-6hc-d6vp-oe08g",
+                               "type":"runsOnPserver",
+                               "url":"services/inventory/relationships/v8/has/215x5m-6hc-d6vp-oe08g",
+                               "source":"services/inventory/v8/vserver/8400",
+                               "target":"services/inventory/v8/pserver/40964272",
+                               "properties":{   
+                                       "is-parent":"true",
+                                       "multiplicity":"many",
+                                       "has-del-target":"true",
+                                       "uses-resource":"true"
+                               }
+                       }
+       Error Response:
+               Code: 400 (BAD REQUEST)
+               Content: Error message describing the bad request failure.
+               Situation: Invalid Payload or schema error.
+
+               Code: 403 (FORBIDDEN)
+               Content: Error message describing the Authorization failure.
+               Situation: Authorization failure.
+
+               Code: 415 (UNSUPPORTED MEDIA TYPE)
+               Situation: Unsupported content type .
+               
+               Code: 500 (Internal Server Error)
+               Content: Error message describing the failure.
+               Situation: Any scenario not covered by the above error codes.
+
+Optionally, an edge can be created by posting to an endpoint which doesn't include the edge type.
+
+       URL: https://<host>:9520/services/inventory/relationships/v8/
+       Method: POST
+       Body:
+               {    
+                       "type":"runsOnPserver",
+                       "source":"services/inventory/v8/vserver/0",
+                       "target":"services/inventory/v8/pserver/7",
+                       "properties":{   
+                               "multiplicity":"many",
+                               "is-parent":true,
+                               "uses-resource":"true",
+                               "has-del-target":"true"
+                       }
+               }
+       Success Response:
+               Code: 201
+               Content: Same as above  
+       Error Response:
+               Code: 400 (BAD REQUEST)
+               Content: Error message describing the bad request failure.
+               Situation: Invalid Payload or schema error.
+
+               Code: 403 (FORBIDDEN)
+               Content: Error message describing the Authorization failure.
+               Situation: Authorization failure.
+
+               Code: 415 (UNSUPPORTED MEDIA TYPE)
+               Situation: Unsupported content type .
+               
+               Code: 500 (Internal Server Error)
+               Content: Error message describing the failure.
+               Situation: Any scenario not covered by the above error codes.
+### Get Edge
+
+       URL: https://<host>:9520/services/inventory/relationships/v8/runsOnPserver/<id>
+       Method: GET
+       Success Response:
+               Code: 200
+               Content:
+                       {   
+                               "id":"215x5m-6hc-d6vp-oe08g",
+                               "type":"runsOnPserver",
+                               "url":"services/inventory/relationships/v8/has/215x5m-6hc-d6vp-oe08g",
+                               "source":"services/inventory/v8/vserver/8400",
+                               "target":"services/inventory/v8/pserver/40964272",
+                               "properties":{   
+                                       "is-parent":"true",
+                                       "multiplicity":"many",
+                                       "has-del-target":"true",
+                                       "uses-resource":"true"
+                               }
+                       }       
+       Error Response:
+               Code: 404 (NOT FOUND)
+               Situation: Reource Not found
+
+               Code: 403 (FORBIDDEN)
+               Content: Error message describing the Authorization failure.
+               Situation: Authorization failure.
+
+               Code: 415 (UNSUPPORTED MEDIA TYPE)
+               Situation: Unsupported content type .
+               
+               Code: 500 (Internal Server Error)
+               Content: Error message describing the failure.
+               Situation: Any scenario not covered by the above error codes.
+
+### Get Edges
+
+       URL: https://<host>:9520/services/inventory/relationships/v8/runsOnPserver
+       Optional Query Param: ?multiplicity=many
+       Method: GET
+       Success Response:
+               Code: 200
+               Content:
+                       [   
+                               {   
+                                       "id":"1crwnu-6hc-d6vp-oe08g",
+                                       "type":"runsOnPserver",
+                                       "url":"services/inventory/relationships/v8/runsOnPserver/1crwnu-6hc-d6vp-oe08g",
+                                       "source":"services/inventory/v8/vserver/8400",
+                                       "target":"services/inventory/v8/pserver/40964272"
+                               },
+                               {   
+                                       "id":"215x5m-6hc-d6vp-oe08g",
+                                       "type":"runsOnPserver",
+                                       "url":"services/inventory/relationships/v8/runsOnPserver/215x5m-6hc-d6vp-oe08g",
+                                       "source":"services/inventory/v8/vserver/8400",
+                                       "target":"services/inventory/v8/pserver/40964272"
+                               }
+                       ]
+       Error Response:
+               Code: 404 (NOT FOUND)
+               Situation: Reource Not found
+
+               Code: 403 (FORBIDDEN)
+               Content: Error message describing the Authorization failure.
+               Situation: Authorization failure.
+
+               Code: 415 (UNSUPPORTED MEDIA TYPE)
+               Situation: Unsupported content type .
+               
+               Code: 500 (Internal Server Error)
+               Content: Error message describing the failure.
+               Situation: Any scenario not covered by the above error codes.           
+
+### Update Edge
+
+The PUT command is used to modify an existing edge.  By default, the edge data is replaced by the content of the payload.  However, the following parameter can be added to the header to perform a PATCH instead of a replace:
+**X-HTTP-Method-Override=Patch**
+
+       URL: https://<host>:9520/services/inventory/relationships/v8/runsOnPserver/<id>
+       Method: PUT
+       Body: (**Note that the source and target can not be modified)
+               {     
+                       "properties":{   
+                               "multiplicity":"many",
+                               "is-parent":true,
+                               "uses-resource":"true",
+                               "has-del-target":"true"
+                       }
+               }
+       Success Response:
+               Code: 200
+               Content: Same as POST   
+       Error Response:
+               Code: 400 (BAD REQUEST)
+               Content: Error message describing the bad request failure.
+               Situation: Invalid Payload or schema error.
+
+               Code: 403 (FORBIDDEN)
+               Content: Error message describing the Authorization failure.
+               Situation: Authorization failure.
+
+               Code: 415 (UNSUPPORTED MEDIA TYPE)
+               Situation: Unsupported content type .
+               
+               Code: 500 (Internal Server Error)
+               Content: Error message describing the failure.
+               Situation: Any scenario not covered by the above error codes.
+
+### Patch Edge
+
+       URL: https://<host>:9520/services/inventory/relationships/v8/runsOnPserver/<id>
+       Method: PATCH (Content-Type header set to application/merge-patch+json)
+       Body:
+               {        
+                       "properties":{   
+                               "multiplicity":"many"
+                       }
+               }
+       Success Response:
+               Code: 200
+               Content: Same as POST           
+       Error Response:
+               Code: 400 (BAD REQUEST)
+               Content: Error message describing the bad request failure.
+               Situation: Invalid Payload or schema error.
+
+               Code: 403 (FORBIDDEN)
+               Content: Error message describing the Authorization failure.
+               Situation: Authorization failure.
+
+               Code: 415 (UNSUPPORTED MEDIA TYPE)
+               Situation: Unsupported content type .
+               
+               Code: 500 (Internal Server Error)
+               Content: Error message describing the failure.
+               Situation: Any scenario not covered by the above error codes.
+### Delete Edge
+
+       URL: https://<host>:9520/services/inventory/relationships/v8/runsOnPserver/<id>
+       Method: DELETE
+       Success Response:
+               Code: 200               
+       Error Response:
+               Code: 404 (NOT FOUND)
+               Situation: Resource not found
+
+               Code: 403 (FORBIDDEN)
+               Content: Error message describing the Authorization failure.
+               Situation: Authorization failure.
+
+               Code: 415 (UNSUPPORTED MEDIA TYPE)
+               Situation: Unsupported content type .
+               
+               Code: 500 (Internal Server Error)
+               Content: Error message describing the failure.
+               Situation: Any scenario not covered by the above error codes.
+               
\ No newline at end of file
diff --git a/ajsc-shared-config/README.txt b/ajsc-shared-config/README.txt
new file mode 100644 (file)
index 0000000..e0f7ff1
--- /dev/null
@@ -0,0 +1,8 @@
+#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+
+The ajsc-shared-config folder is included in the service project to provide the functionality of the AJSC_SHARED_CONFIG 
+location that will exist in CSI envs. This includes the logback.xml for logging configurations, and some csm related 
+artifacts necessary for proper functionality of the csm framework within the CSI env. Within the 2 profiles that can 
+be utilized to run the AJSC locally, "runLocal" and "runAjsc", the system propery, "AJSC_SHARED_CONFIG", has been set
+to point to this directory. The files in this folder will NOT be copied/moved anywhere within the AJSC SWM package. These 
+files will already be in existence within the CSI env.
\ No newline at end of file
diff --git a/ajsc-shared-config/etc/aft.properties b/ajsc-shared-config/etc/aft.properties
new file mode 100644 (file)
index 0000000..95c7762
--- /dev/null
@@ -0,0 +1,15 @@
+#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+# Flow test 319
+# The DEFAULT setup for this file is for deployment to soa cloud node which will use the "bundleconfig/etc/spm2.jks" location
+# For Testing Locally, you can set the system property, csiEnable=true, found within bundleconfig-local/etc/sysprops/sys-props.properties
+# and switch com.att.aft.keyStore and com.att.aft.trustStore values commented out below to "ajsc-shared-config/etc/spm2.jks"
+
+#replace proper values for the dummy values.
+com.att.aft.discovery.client.environment=TEST
+com.att.aft.discovery.client.latitude=35.318900
+com.att.aft.discovery.client.longitude=-80.762200
+com.att.aft.alias=fusionbus
+com.att.aft.keyStore=bundleconfig/etc/key.jks
+com.att.aft.keyStorePassword=password
+com.att.aft.trustStore=bundleconfig/etc/key.jks
+com.att.aft.trustStorePassword=password
diff --git a/ajsc-shared-config/etc/basic-logback_root_logger_level_off.xml b/ajsc-shared-config/etc/basic-logback_root_logger_level_off.xml
new file mode 100644 (file)
index 0000000..4ebe2db
--- /dev/null
@@ -0,0 +1,87 @@
+<!-- 
+ Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+-->
+<configuration scan="true" scanPeriod="3 seconds" debug="true">
+       <property name="logDirectory" value="${AJSC_HOME}/log" />
+       <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+               <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+                       <level>ERROR</level>
+               </filter>
+               <encoder>
+                       <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{1024} - %msg%n
+                       </pattern>
+               </encoder>
+       </appender>
+
+       <appender name="INFO"
+               class="ch.qos.logback.core.rolling.RollingFileAppender">
+               <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+                       <level>DEBUG</level>
+               </filter>
+               <file>${logDirectory}/info_ajsc.log</file>
+               <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+                       <fileNamePattern>${logDirectory}/info_ajsc.%i.log.zip
+                       </fileNamePattern>
+                       <minIndex>1</minIndex>
+                       <maxIndex>9</maxIndex>
+               </rollingPolicy>
+               <triggeringPolicy
+                       class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+                       <maxFileSize>5MB</maxFileSize>
+               </triggeringPolicy>
+               <encoder>
+                       <pattern>"%d [%thread] %-5level %logger{1024} - %msg%n"</pattern>
+               </encoder>
+       </appender>
+       <appender name="ERROR"
+               class="ch.qos.logback.core.rolling.RollingFileAppender">
+               <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+                       <level>ERROR</level>
+               </filter>
+               <file>${logDirectory}/error_ajsc.log</file>
+               <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+                       <fileNamePattern>${logDirectory}/error_ajsc.%i.log.zip
+                       </fileNamePattern>
+                       <minIndex>1</minIndex>
+                       <maxIndex>9</maxIndex>
+               </rollingPolicy>
+               <triggeringPolicy
+                       class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+                       <maxFileSize>5MB</maxFileSize>
+               </triggeringPolicy>
+               <encoder>
+                       <!-- <pattern>"%d{HH:mm:ss.SSS} [%thread] %-5level %logger{1024} - %msg%n"</pattern> -->
+                       <pattern>"%d [%thread] %-5level %logger{1024} - %msg%n"</pattern>
+               </encoder>
+       </appender>
+
+       <appender name="AJSC-AUDIT" class="ch.qos.logback.classic.net.SyslogAppender">
+               <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+                       <level>INFO</level>
+               </filter>
+               <syslogHost>localhost</syslogHost>
+               <facility>USER</facility>
+               <!-- Note the colon character below - it is important part of "TAG" message 
+                       format You need a colon to determine where the TAG field ends and the CONTENT 
+                       begins -->
+               <suffixPattern>AJSC_AUDIT: [%thread] [%logger] %msg</suffixPattern>
+       </appender>
+       <appender name="CONTROLLER-AUDIT" class="ch.qos.logback.classic.net.SyslogAppender">
+               <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+                       <level>INFO</level>
+               </filter>
+               <syslogHost>localhost</syslogHost>
+               <facility>USER</facility>
+               <!-- Note the colon character below - it is important part of "TAG" message 
+                       format You need a colon to determine where the TAG field ends and the CONTENT 
+                       begins -->
+               <suffixPattern>AJSC_AUDIT: [%thread] [%logger] mdc:[%mdc] %msg
+               </suffixPattern>
+       </appender>
+
+       <root level="off">
+               <appender-ref ref="ERROR" />
+               <appender-ref ref="INFO" />
+               <appender-ref ref="STDOUT" />
+       </root>
+</configuration>
diff --git a/ajsc-shared-config/etc/logback.xml b/ajsc-shared-config/etc/logback.xml
new file mode 100644 (file)
index 0000000..5972f55
--- /dev/null
@@ -0,0 +1,212 @@
+<configuration scan="true" scanPeriod="3 seconds" debug="false">
+  <!--<jmxConfigurator /> -->
+  <!-- directory path for all other type logs -->
+  
+  <property name="logDir"  value="${AJSC_HOME}/logs" />
+  
+  
+  <!--  specify the component name 
+       <ECOMP-component-name>::= "MSO" | "DCAE" | "ASDC " | "AAI" |"Policy" | "SDNC" | "AC"  -->
+  <property name="componentName" value="AAI-CRUD" />
+  
+  <!--  default eelf log file names -->
+  <property name="generalLogName" value="error" />
+  <property name="metricsLogName" value="metrics" />
+  <property name="auditLogName" value="audit" />
+  <property name="debugLogName" value="debug" />
+  
+  <property name="errorLogPattern" value="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}|%mdc{RequestId}|%thread|Crud-Service|%mdc{PartnerName}|%logger||%.-5level|%msg%n" />
+  <property name="auditMetricPattern" value="%m%n" />
+
+  <property name="logDirectory" value="${logDir}/${componentName}" />
+  
+  <!-- Example evaluator filter applied against console appender -->
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>${errorLogPattern}</pattern>
+    </encoder>
+  </appender>
+
+  <!-- ============================================================================ -->
+  <!-- EELF Appenders -->
+  <!-- ============================================================================ -->
+  
+  <!-- The EELFAppender is used to record events to the general application 
+       log -->
+  
+  <appender name="EELF"
+            class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <file>${logDirectory}/${generalLogName}.log</file>
+    <rollingPolicy
+        class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+      <fileNamePattern>${logDirectory}/${generalLogName}.%d{yyyy-MM-dd}.log.zip
+      </fileNamePattern>
+      <maxHistory>60</maxHistory>
+    </rollingPolicy>
+    <encoder>
+      <pattern>${errorLogPattern}</pattern>
+    </encoder>
+  </appender>
+  <appender name="asyncEELF" class="ch.qos.logback.classic.AsyncAppender">
+    <!-- deny all events with a level below INFO, that is TRACE and DEBUG -->
+    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+      <level>INFO</level>
+    </filter>
+    <queueSize>256</queueSize>
+    <appender-ref ref="EELF" />
+  </appender>
+
+  
+  <!-- EELF Audit Appender. This appender is used to record audit engine 
+       related logging events. The audit logger and appender are specializations 
+       of the EELF application root logger and appender. This can be used to segregate 
+       Policy engine events from other components, or it can be eliminated to record 
+       these events as part of the application root log. -->
+  
+  <appender name="EELFAudit"
+            class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <file>${logDirectory}/${auditLogName}.log</file>
+    <rollingPolicy
+        class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+      <fileNamePattern>${logDirectory}/${auditLogName}.%d{yyyy-MM-dd}.log.zip
+      </fileNamePattern>
+      <maxHistory>60</maxHistory>
+    </rollingPolicy>
+    <encoder>
+      <pattern>${auditMetricPattern}</pattern>
+    </encoder>
+  </appender>
+  <appender name="asyncEELFAudit" class="ch.qos.logback.classic.AsyncAppender">
+    <queueSize>256</queueSize>
+    <appender-ref ref="EELFAudit" />
+  </appender>
+  
+  <appender name="EELFMetrics"
+            class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <file>${logDirectory}/${metricsLogName}.log</file>
+    <rollingPolicy
+        class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+      <fileNamePattern>${logDirectory}/${metricsLogName}.%d{yyyy-MM-dd}.log.zip
+      </fileNamePattern>
+      <maxHistory>60</maxHistory>
+    </rollingPolicy>
+    <encoder>
+      <!-- <pattern>"%d{HH:mm:ss.SSS} [%thread] %-5level %logger{1024} - 
+           %msg%n"</pattern> -->
+      <pattern>${auditMetricPattern}</pattern>
+    </encoder>
+  </appender>
+  
+  
+  <appender name="asyncEELFMetrics" class="ch.qos.logback.classic.AsyncAppender">
+    <queueSize>256</queueSize>
+    <appender-ref ref="EELFMetrics"/>
+  </appender>
+  
+  <appender name="EELFDebug"
+            class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <file>${logDirectory}/${debugLogName}.log</file>
+    <rollingPolicy
+        class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+      <fileNamePattern>${logDirectory}/${debugLogName}.%d{yyyy-MM-dd}.log.zip
+      </fileNamePattern>
+      <maxHistory>60</maxHistory>
+    </rollingPolicy>
+    <encoder>
+      <pattern>${errorLogPattern}</pattern>
+    </encoder>
+  </appender>
+  
+  <appender name="asyncEELFDebug" class="ch.qos.logback.classic.AsyncAppender">
+    <queueSize>256</queueSize>
+    <appender-ref ref="EELFDebug" />
+    <includeCallerData>false</includeCallerData>
+  </appender>
+  
+  
+  <!-- ============================================================================ -->
+  <!--  EELF loggers -->
+  <!-- ============================================================================ -->
+  <logger name="com.att.eelf" level="info" additivity="false">
+    <appender-ref ref="asyncEELF" />
+    <appender-ref ref="asyncEELFDebug" />
+  </logger>
+
+  <logger name="com.att.eelf.security" level="info" additivity="false">
+    <appender-ref ref="asyncEELFSecurity" /> 
+  </logger>
+  <logger name="com.att.eelf.perf" level="info" additivity="false">
+    <appender-ref ref="asyncEELFPerformance" />
+  </logger>
+  <logger name="com.att.eelf.server" level="info" additivity="false">
+    <appender-ref ref="asyncEELFServer" />
+  </logger>
+  <logger name="com.att.eelf.policy" level="info" additivity="false">
+    <appender-ref ref="asyncEELFPolicy" />
+  </logger>
+  <logger name="com.att.eelf.audit" level="info" additivity="false">
+    <appender-ref ref="asyncEELFAudit" />
+  </logger>
+  <logger name="com.att.eelf.metrics" level="info" additivity="false">
+    <appender-ref ref="asyncEELFMetrics" />
+  </logger>
+  
+  <!-- Spring related loggers -->
+  <logger name="org.springframework" level="WARN" />
+  <logger name="org.springframework.beans" level="WARN" />
+  <logger name="org.springframework.web" level="WARN" />
+  <logger name="com.blog.spring.jms" level="WARN" />
+
+  <!-- AJSC Services (bootstrap services) -->
+  <logger name="ajsc" level="WARN" />
+  <logger name="ajsc.RouteMgmtService" level="WARN" />
+  <logger name="ajsc.ComputeService" level="WARN" />
+  <logger name="ajsc.VandelayService" level="WARN" />
+  <logger name="ajsc.FilePersistenceService" level="WARN" />
+  <logger name="ajsc.UserDefinedJarService" level="WARN" />
+  <logger name="ajsc.UserDefinedBeansDefService" level="WARN" />
+  <logger name="ajsc.LoggingConfigurationService" level="WARN" />
+  
+  <!-- AJSC related loggers (DME2 Registration, csi logging, restlet, servlet 
+       logging) -->
+  <logger name="ajsc.utils" level="WARN" />
+  <logger name="ajsc.utils.DME2Helper" level="WARN" />
+  <logger name="ajsc.filters" level="WARN" />
+  <logger name="ajsc.beans.interceptors" level="WARN" />
+  <logger name="ajsc.restlet" level="WARN" />
+  <logger name="ajsc.servlet" level="WARN" />
+  <logger name="com.att" level="INFO" />
+  <logger name="com.att.ajsc.csi.logging" level="WARN" />
+  <logger name="com.att.ajsc.filemonitor" level="WARN" />
+
+  <!-- CRUD Service loggers -->
+  <logger name="org.openecomp.crud" level="INFO" />
+
+  <!-- Other Loggers that may help troubleshoot -->
+  <logger name="net.sf" level="WARN" />
+  <logger name="org.apache" level="WARN" />
+  <logger name="org.apache.commons.httpclient" level="WARN" />
+  <logger name="org.apache.commons" level="WARN" />
+  <logger name="org.apache.coyote" level="WARN" />
+  <logger name="org.apache.jasper" level="WARN" />
+
+  <!-- Camel Related Loggers (including restlet/servlet/jaxrs/cxf logging. 
+       May aid in troubleshooting) -->
+  <logger name="org.apache.camel" level="WARN" />
+  <logger name="org.apache.cxf" level="WARN" />
+  <logger name="org.apache.camel.processor.interceptor" level="WARN" />
+  <logger name="org.apache.cxf.jaxrs.interceptor" level="WARN" />
+  <logger name="org.apache.cxf.service" level="WARN" />
+  <logger name="org.restlet" level="WARN" />
+  <logger name="org.apache.camel.component.restlet" level="WARN" />
+
+  <!-- logback internals logging -->
+  <logger name="ch.qos.logback.classic" level="WARN" />
+  <logger name="ch.qos.logback.core" level="WARN" />
+
+  <root>
+    <appender-ref ref="asyncEELF" /> 
+    <!-- <appender-ref ref="asyncEELFDebug" /> -->
+  </root>
+  
+</configuration>
diff --git a/ajsc-shared-config/etc/spm2.jks b/ajsc-shared-config/etc/spm2.jks
new file mode 100644 (file)
index 0000000..8ff2a00
Binary files /dev/null and b/ajsc-shared-config/etc/spm2.jks differ
diff --git a/antBuild/build.xml b/antBuild/build.xml
new file mode 100644 (file)
index 0000000..ab0f890
--- /dev/null
@@ -0,0 +1,231 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+ Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+-->
+<project>
+       <target name="runLocal">
+               <java dir="${basedir}" fork="yes" newenvironment="true"
+                       failonerror="true" classname="com.att.ajsc.runner.Runner">
+                       <classpath
+                               path="${classpath}:${basedir}/ajsc-shared-config/etc:${runAjscHome}/lib/ajsc-runner-${ajscRuntimeVersion}.jar" />
+
+                       <!-- Windows Users may need to add a jvmarg arg to create a temp directory 
+                               properly. -->
+                       <!-- <jvmarg value="-Djava.io.tmpdir=C:/yourTempDirectory"/> -->
+
+                       <!-- Uncomment the following 2 jvmarg values to enable Remote Debugging. 
+                        -->
+                       <!-- <jvmarg value="-Xdebug" /> -->
+                       <!-- <jvmarg value="-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5432" 
+                               /> -->
+
+                       <jvmarg value="-XX:MaxPermSize=512m" />
+                       <jvmarg value="-Xmx1024m" />
+
+                       <!-- Main ajsc Variables below (Variables necessary for proper startup 
+                               of AJSC) -->
+                       <env key="AJSC_HOME" value="${runAjscHome}" />
+                       <sysproperty key="AJSC_HOME" value="${runAjscHome}" />
+                       <!-- you may specify any external location for AJSC_CONF_HOME where etc 
+                               folder & all other configs can be found under it. If not specified, it will 
+                               default to AJSC_HOME -->
+                       <sysproperty key="AJSC_CONF_HOME" value="${basedir}/bundleconfig-local" />
+                       <sysproperty key="AJSC_SHARED_CONFIG" value="${basedir}/ajsc-shared-config" />
+
+                       <!-- Location of logback.xml file used for logging configurations. Please, 
+                               note, when deploying a service to either CSI or NON-CSI environment, this 
+                               system property will be set in sys-props.properties file. We are setting 
+                               it here for running locally due to the ease of use of maven variable for 
+                               basedir. -->
+                       <sysproperty key="logback.configurationFile"
+                               value="${basedir}/ajsc-shared-config/etc/logback.xml" />
+
+                       <!-- Setting system properties for the AJSC external libs and properties 
+                               folders below. When deploying to a node, these properties will be set within 
+                               the bundleconfig/etc/sysprops/sys-props.properties file. However, when running 
+                               locally, the ${basedir} substitution works more efficiently in this manner. -->
+                       <sysproperty key="AJSC_EXTERNAL_LIB_FOLDERS" value="${basedir}/target/commonLibs" />
+                       <sysproperty key="AJSC_EXTERNAL_PROPERTIES_FOLDERS"
+                               value="${basedir}/ajsc-shared-config/etc" />
+
+                       <!-- End of Main ajsc Variables below (Variables necessary for proper 
+                               startup of AJSC) -->
+
+                       <!-- Uncomment the following line to add oauthentication to your Service -->
+                       <!-- <sysproperty key="spring.profiles.active" value="oauth" /> -->
+
+                       <!-- If using Cassandra as Database, Enter the ip/host and port below 
+                               based on your known configuration -->
+                       <!-- <sysproperty key="cassandra.ip" value="hostname" /> -->
+                       <!-- <sysproperty key="cassandra.port" value="9042" /> -->
+
+                       <!-- The APP_SERVLET_URL_PATTERN variable is defaulted to "/services" 
+                               within the initial configuration of the AJSC. If you are changing the CamelServlet 
+                               Filter within the ajsc-override-web.xml, you should use that url-pattern 
+                               here. This is necessary to properly register your service with dme2. An empty 
+                               value, "", is used when NO value is wanted (url-pattern would be /* for CamelServlet 
+                               Filter) -->
+                       <!-- As of 4.5.1, this property is no longer needed -->
+                       <!-- <sysproperty key="APP_SERVLET_URL_PATTERN" value="/services" /> -->
+
+                       <!-- GRM/DME2 System Properties below -->
+                       <sysproperty key="AJSC_SERVICE_NAMESPACE" value="${module.ajsc.namespace.name}" />
+                       <sysproperty key="AJSC_SERVICE_VERSION" value="${module.ajsc.namespace.version}" />
+                       <sysproperty key="SOACLOUD_SERVICE_VERSION" value="${project.version}" />
+                       <!-- End of GRM/DME2 System Property Variables -->
+
+                       <!-- The following server.port variable was necessary for the proper registration 
+                               of the AJSC to dme2. This value may still need to be used if the Developer 
+                               is hardcoding their port (example: 8080). Then, the server.port value="8080". 
+                               The default functionality for the AJSC is to use EPHEMERAL ports. In this 
+                               case, you do NOT need to set the server.port value. The AJSC will find the 
+                               proper port value and register to dme2 correctly -->
+                       <!-- <sysproperty key="server.port" value="${serverPort}" /> -->
+
+                       <!-- Command Line Arguments to add to the java command. Here, you can 
+                               specify the port as well as the Context you want your service to run in. 
+                               Use context=/ to run in an unnamed Context (Root Context). The default configuration 
+                               of the AJSC is to run under the /ajsc Context. Setting the port here can 
+                               aid during the development phase of your service. However, you can leave 
+                               this argument out entirely, and the AJSC will default to using an Ephemeral 
+                               port. -->
+                       <arg line="context=/ port=${serverPort} sslport=${sslport}" />
+               </java>
+       </target>
+       <target name="prep_home_directory_for_swm_pkgcreate">
+
+<!-- ********* GENERATE CADI KEY AND ENCRYPTED PASSWORD ***********
+     
+            Uncomment the following if your cadi key get corrupted , It would 
+                       generate the Cadi key and password in the package phase and keep the key 
+                       in the 'src/main/config/ajscKey' and password in the bottom of cadi.properties(you 
+                       need to modify the 'aaf_pass' variable with this value . Plese modify the 
+                       template.cadi.properties as well before uploading to SOA node 
+-->
+
+<!-- 
+               <java jar="${basedir}/target/userjars/cadi-core-1.2.5.jar" fork="true"> 
+               <arg value="keygen" /> <arg value="src/main/config/ajscKey" /> 
+               </java> 
+               
+               <echo>***Cadi Key file generated ****</echo> 
+               
+               <java jar="${basedir}/target/userjars/cadi-core-1.2.5.jar" 
+               fork="true" append="true" output="${basedir}/src/main/config/cadi.properties"> 
+               <arg value="digest" /> <arg value="ajscRocks!" /> <arg value="src/main/config/ajscKey" 
+               /> 
+               </java> 
+               
+-->
+
+
+
+               <!-- These tasks are copying contents from the installHomeDirectory into 
+                       the eventual $AJSC_HOME directory for running locally and soa cloud installation -->
+               <echo message="ENTERING 'prep_home_directory_for_swm_pkgcreate' ant tasks" />
+
+               <!-- Please, NOTE: The ajsc-archetype is setup for a default CSI Env deployment. 
+                       If you are deploying to a CSI Env, you should NOT have to change anything 
+                       within this build file. However, if you are NOT deploying to a CSI Env, you 
+                       should comment OUT the CSI related portion of this build.xml. -->
+
+               <!-- The following code snippet is copying the bundleconfig-csi directory 
+                       to the proper installation/bundleconfig directory used in CSI envs. If you 
+                       are NOT installing to a CSI node, you should comment out (or delete) the 
+                       following snippet, and uncomment the NON-CSI copy task to copy EVERYTHING 
+                       to the installation/bundleconfig directory. -->
+
+               <!-- CSI related bundleconfig copy task. If you are NOT deploying to a 
+                       CSI Env, please COMMENT OUT or delete the following copy task code snippet. -->
+               <!--<copy toDir="${basedir}/target/swm/package/nix/dist_files${distFilesRoot}/bundleconfig" 
+                       failonerror="true"> <fileset dir="${basedir}/bundleconfig-csi" /> </copy> -->
+               <!-- End of CSI related bundleconfig copy task -->
+
+               <!-- NOTE: If you are NOT deploying to CSI environment, and you are NOT 
+                       using an AJSC_SHARED_CONFIG location on a node, you should go ahead and copy 
+                       EVERYTHING from bundleconfig and ajsc-shared-config (logback.xml) directory 
+                       to utilize proper logging from logback.xml. Simply, uncomment the following 
+                       code snippet below to copy EVERYTHING and comment out the CSI related build 
+                       script above. -->
+               <!-- NON-CSI related build copy task. Please, uncomment the following code 
+                       snippet to deploy the proper artifacts to a NON-CSI Env. -->
+               <copy
+                       toDir="${basedir}/target/swm/package/nix/dist_files${distFilesRoot}/bundleconfig"
+                       failonerror="true">
+                       <fileset dir="${basedir}/bundleconfig-local" includes="**/**" />
+               </copy>
+               <copy
+                       toDir="${basedir}/target/swm/package/nix/dist_files${distFilesRoot}/bundleconfig/etc"
+                       failonerror="true">
+                       <fileset dir="${basedir}/ajsc-shared-config/etc" includes="**/**" />
+               </copy>
+               <!-- End of NON-CSI related build copy task. -->
+
+               <!-- Copying any zips (deployment packages) to $AJSC_HOME/services for 
+                       auto-deployment -->
+               <copy
+                       toDir="${basedir}/target/swm/package/nix/dist_files${distFilesRoot}/services"
+                       failonerror="false">
+                       <fileset dir="${basedir}/services" includes="*.zip" />
+               </copy>
+
+               <!-- Copying runtimeEnvironment zip file to $AJSC_HOME/runtime and renaming 
+                       runtimeEnvironment.zip for proper auto-deployment of ajsc services. 
+               <copy
+                       tofile="${basedir}/target/swm/package/nix/dist_files${distFilesRoot}/runtime/runtimeEnvironment.zip">
+                       <fileset dir="target" includes="*-runtimeEnvironment.zip" />
+               </copy>-->
+
+               <!-- Copying dependencies from the service project (not provided by AJSC 
+                       Container) to the $AJSC_HOME/extJars folder to be accessible on the classpath -->
+               <copy
+                       toDir="${basedir}/target/swm/package/nix/dist_files${distFilesRoot}/extJars"
+                       failonerror="false">
+                       <fileset dir="target/userjars" includes="*" />
+               </copy>
+
+               <!-- extApps directory MUST be created for ajsc-runner to run correctly, 
+                       even if empty. DO NOT REMOVE!!! -->
+               <!-- extApps directory created to deploy other war files on startup or 
+                       hot deploy War files after ajsc starts up. -->
+               <mkdir
+                       dir="${basedir}/target/swm/package/nix/dist_files${distFilesRoot}/extApps" />
+
+               <!-- Copying any extra wars to $AJSC_HOME/extApps to be deployed within 
+                       AJSC -->
+               <copy
+                       toDir="${basedir}/target/swm/package/nix/dist_files${distFilesRoot}/extApps"
+                       failonerror="false">
+                       <fileset dir="${basedir}/src/main/resources/extApps"
+                               includes="*" />
+               </copy>
+
+               <!-- staticContent folder is for serving static content within an ajsc 
+                       service. Any static content to be served will be copyied to the ultimate 
+                       $AJSC_HOME/staticContent folder and can be served with the att-static-content 
+                       camel component. -->
+               <!-- Uncomment the following snippet to copy items from staticContent folder 
+                       to ultimate $AJSC_HOME/staticConent -->
+               <!-- <copy toDir="${basedir}/target/swm/package/nix/dist_files${distFilesRoot}/staticContent" 
+                       failonerror="false"> <fileset dir="${basedir}/staticContent" includes="**/**" 
+                       /> </copy> -->
+
+               <!-- Copying extra jar files that have been labeled as dependencies in 
+                       service project to /extJars folder to be made available on the classpath 
+                       for your service -->
+               <copy
+                       toDir="${basedir}/target/swm/package/nix/dist_files${distFilesRoot}/extJars"
+                       failonerror="false">
+                       <fileset dir="target" includes="*.jar" />
+               </copy>
+
+               <!-- Copying deployment packages created within the project to the $AJSC_HOME/services 
+                       folder to be auto deployed. -->
+               <copy
+                       toDir="${basedir}/target/swm/package/nix/dist_files${distFilesRoot}/services">
+                       <fileset dir="target" includes="*.zip" excludes="*-runtimeEnvironment.zip" />
+               </copy>
+
+               <echo message="EXITING 'prep_assembly_output_for_swm_plugin' ant tasks" />
+       </target>
+</project>
diff --git a/bundleconfig-local/README.txt b/bundleconfig-local/README.txt
new file mode 100644 (file)
index 0000000..37f2670
--- /dev/null
@@ -0,0 +1,2 @@
+#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+The bundleconfig-local directory contains the necessary configuration files 
\ No newline at end of file
diff --git a/bundleconfig-local/RELEASE_NOTES.txt b/bundleconfig-local/RELEASE_NOTES.txt
new file mode 100644 (file)
index 0000000..3cc5590
--- /dev/null
@@ -0,0 +1,2 @@
+#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+Place Release Notes here to provide updated Release information 
\ No newline at end of file
diff --git a/bundleconfig-local/etc/appprops/AAFUserRoles.properties b/bundleconfig-local/etc/appprops/AAFUserRoles.properties
new file mode 100644 (file)
index 0000000..adb7a10
--- /dev/null
@@ -0,0 +1,13 @@
+#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+
+#If using AAF for Role based authentication/authorization, define your routes/services which will utilize AAF. The AJSC will
+#read this file and protect the routes given with the AAF role defined.
+
+#The following example would protect the JAXRS echo example service provided with the archetype.
+#/services/${namespace}/v1/jaxrs-services/jaxrsExample/echo/*=com.att.ajsc.myper|mymachine|manage
+
+#The following example would protect ALL AJSC services running within your project.
+#/**=com.att.ajsc.myperm|mymachine|manage
+
+#The following example would protect ALL REST services utilizing the Camel restlet routes.
+#/rest/**=com.att.ajsc.myperm|mymachine|manage
diff --git a/bundleconfig-local/etc/appprops/PostProcessorInterceptors.properties b/bundleconfig-local/etc/appprops/PostProcessorInterceptors.properties
new file mode 100644 (file)
index 0000000..08ffefa
--- /dev/null
@@ -0,0 +1,3 @@
+#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+#This properties file is for defining any PostProcessorInterceptors that have been created for your AJSC service.
+
diff --git a/bundleconfig-local/etc/appprops/PreProcessorInterceptors.properties b/bundleconfig-local/etc/appprops/PreProcessorInterceptors.properties
new file mode 100644 (file)
index 0000000..1383071
--- /dev/null
@@ -0,0 +1,4 @@
+#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+#This properties file is for defining any PreProcessorInterceptors that have been created for your AJSC service. 
+
+/**=com.att.ajsc.csi.restmethodmap.RestMethodMapInterceptor
diff --git a/bundleconfig-local/etc/appprops/app-intercepts.properties b/bundleconfig-local/etc/appprops/app-intercepts.properties
new file mode 100644 (file)
index 0000000..dada69c
--- /dev/null
@@ -0,0 +1,8 @@
+#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+
+#This is where all your application intercept strategies must be configured. AJSC reads this property file and adds
+#the list of intercepts specified here to the camel context. This can be useful for accessing every exchange object transferred from/to
+#each endpoint in the request/response flow and can allow for more precise debugging and/or processing of the exchange. 
+
+#e.g. 
+#intercepts=org.openecomp.crud.JaxrsEchoService,packagename.class1name,packagename.class2name
diff --git a/bundleconfig-local/etc/appprops/methodMapper.properties b/bundleconfig-local/etc/appprops/methodMapper.properties
new file mode 100644 (file)
index 0000000..bf51749
--- /dev/null
@@ -0,0 +1,30 @@
+//
+//Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+//     Json object holds the method mapping.Update the JSON object with the proper route to logical mapping based 
+//     on the example provided below : 
+//     "helloWorld"  = Service Name
+//     "method"   = http method
+//     "url" = the url component from the route
+//     "logicalName"=  When a combination of method and url from the route matches the json object , 
+//     the logical name is put in the http header as "x-CSI-ServiceName" and "x-CSI-MethodName" 
+//     "dme2url"= if provided it register the endpoint to GRM, it is optional. This is useful for JAX-RS services.
+  
+{
+    "helloWorld": [
+        {
+            "method": "get",
+            "url": "/services/crud-api/v1/echo-service/echo/{input}",
+            "logicalName": "GetEcho(Logical)",
+            "dme2url": "/services/crud-api/v1/echo-service/echo/{input}"
+        }
+    ],
+            "errormessage":
+           [
+               {
+                       "method": "get",
+                       "url": "/services/crud-api/v1/errorMessageLookupService2",
+                       "logicalName": "setCAETHeaders(Logical)"
+               }        
+           
+           ]
+}
diff --git a/bundleconfig-local/etc/sysprops/sys-props.properties b/bundleconfig-local/etc/sysprops/sys-props.properties
new file mode 100644 (file)
index 0000000..b816a44
--- /dev/null
@@ -0,0 +1,118 @@
+#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+#This file is used for defining AJSC system properties for different configuration schemes and is necessary for the AJSC to run properly.
+#The sys-props.properties file is used for running locally. The template.sys-props.properties file will be used when deployed
+#to a SOA/CSI Cloud node. 
+
+#AJSC System Properties. The following properties are required for ALL AJSC services. If you are adding System Properties for your
+#particular service, please add them AFTER all AJSC related System Properties. 
+
+#For Cadi Authorization, use value="authentication-scheme-1
+CadiAuthN=authentication-scheme-1
+
+#For Basic Authorization, use value="authentication-scheme-1
+authN=authentication-scheme-2
+
+#Persistence used for AJSC meta-data storage. For most environments, "file" should be used.
+ajscPersistence=file
+
+#For Direct Invocation to be enabled (values=true/false)
+directInvocationEnable=false
+
+# If using hawtio for local development, these properties will allow for faster server startup and usage for local development
+
+hawtio.authenticationEnabled=false
+hawtio.config.pullOnStartup=false
+
+#Removes the extraneous restlet console output
+org.restlet.engine.loggerFacadeClass=org.restlet.ext.slf4j.Slf4jLoggerFacade
+
+#server.host property to be enabled for local DME2 related testing
+#server.host=<Your network IP address> 
+
+#Enable/disable SSL (values=true/false). This property also determines which protocol to use (https if true, http otherwise), to register services into GRM through DME2.
+enableSSL=false
+
+
+#Enable/disable EJB Container
+ENABLE_EJB=false
+
+#Enable/disable OSGI
+isOSGIEnable=false
+
+#Generate/Skip api docs
+isApiDoc=false
+
+#CSI related variables for CSM framework
+csm.hostname=servername
+
+
+#SOA_CLOUD_ENV is used to register your service with dme2 and can be turned off for local development (values=true/false).
+SOA_CLOUD_ENV=false
+
+#CONTINUE_ON_LISTENER_EXCEPTION will exit the application if there is a DME2 exception at the time of registration.
+CONTINUE_ON_LISTENER_EXCEPTION=false
+
+#Jetty Container ThreadCount Configuration Variables
+AJSC_JETTY_ThreadCount_MIN=1
+AJSC_JETTY_ThreadCount_MAX=200
+AJSC_JETTY_IDLETIME_MAX=3000
+
+#Camel Context level default threadPool Profile configuration
+CAMEL_POOL_SIZE=10
+CAMEL_MAX_POOL_SIZE=20
+CAMEL_KEEP_ALIVE_TIME=60
+CAMEL_MAX_QUEUE_SIZE=1000
+
+#GRM/DME2 System Properties
+AFT_DME2_CONN_IDLE_TIMEOUTMS=5000
+AJSC_ENV=SOACLOUD
+
+SOACLOUD_NAMESPACE=com.att.ajsc
+SOACLOUD_ENV_CONTEXT=DEV
+SOACLOUD_PROTOCOL=http
+SOACLOUD_ROUTE_OFFER=DEFAULT
+
+AFT_LATITUDE=23.4
+AFT_LONGITUDE=33.6
+AFT_ENVIRONMENT=AFTUAT
+
+#Restlet Component Default Properties
+RESTLET_COMPONENT_CONTROLLER_DAEMON=true
+RESTLET_COMPONENT_CONTROLLER_SLEEP_TIME_MS=100
+RESTLET_COMPONENT_INBOUND_BUFFER_SIZE=8192
+RESTLET_COMPONENT_MIN_THREADS=1
+RESTLET_COMPONENT_MAX_THREADS=10
+RESTLET_COMPONENT_LOW_THREADS=8
+RESTLET_COMPONENT_MAX_QUEUED=0
+RESTLET_COMPONENT_MAX_CONNECTIONS_PER_HOST=-1
+RESTLET_COMPONENT_MAX_TOTAL_CONNECTIONS=-1
+RESTLET_COMPONENT_OUTBOUND_BUFFER_SIZE=8192
+RESTLET_COMPONENT_PERSISTING_CONNECTIONS=true
+RESTLET_COMPONENT_PIPELINING_CONNECTIONS=false
+RESTLET_COMPONENT_THREAD_MAX_IDLE_TIME_MS=60000
+RESTLET_COMPONENT_USE_FORWARDED_HEADER=false
+RESTLET_COMPONENT_REUSE_ADDRESS=true
+
+#Externalized jar and properties file location. In CSI environments, there are a few libs that have been externalized to aid
+#in CSTEM maintenance of the versions of these libs. The most important to the AJSC is the DME2 lib. Not only is this lib necessary
+#for proper registration of your AJSC service on a node, but it is also necessary for running locally as well. Another framework
+#used in CSI envs is the CSM framework. These 2 framework libs are shown as "provided" dependencies within the pom.xml. These
+#dependencies will be copied into the target/commonLibs folder with the normal "mvn clean package" goal of the AJSC. They will
+#then be added to the classpath via AJSC_EXTERNAL_LIB_FOLDERS system property. Any files (mainly property files) that need
+#to be on the classpath should be added to the AJSC_EXTERNAL_PROPERTIES_FOLDERS system property. The default scenario when 
+#testing your AJSC service locally will utilize the target/commonLibs directory for DME2 and CSM related artifacts and 2 
+#default csm properties files will be used for local testing with anything CSM knorelated.
+#NOTE: we are using maven-replacer-plugin to replace "(doubleUnderscore)basedir(doubleUnderscore)" with ${basedir} within the 
+#target directory for running locally. Multiple folder locations can be separated by the pipe ("|") character.
+#Please, NOTE: for running locally, we are setting this system property in the antBuild/build.xml "runLocal" target and in the 
+#"runAjsc" profile within the pom.xml. This is to most effectively use maven variables (${basedir}, most specifically. Therefore,
+#when running locally, the following 2 properties should be set within the profile(s) themselves. 
+#Example: target/commonLibs|target/otherLibs
+#AJSC_EXTERNAL_LIB_FOLDERS=__basedir__/target/commonLibs
+#AJSC_EXTERNAL_PROPERTIES_FOLDERS=__basedir__/ajsc-shared-config/etc
+#End of AJSC System Properties
+
+#Service System Properties. Please, place any Service related System Properties below.
+KEY_STORE_PASSWORD=OBF:1i9a1u2a1unz1lr61wn51wn11lss1unz1u301i6o
+KEY_MANAGER_PASSWORD=OBF:1i9a1u2a1unz1lr61wn51wn11lss1unz1u301i6o
+
diff --git a/pom.xml b/pom.xml
new file mode 100644 (file)
index 0000000..dc0d70c
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,518 @@
+<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>
+
+    <parent>
+        <artifactId>ajsc-archetype-parent</artifactId>
+        <groupId>com.att.ajsc</groupId>
+        <version>2.0.0</version>
+    </parent>
+    <groupId>org.onap.aai.gizmo</groupId>
+    <artifactId>gizmo</artifactId>
+    <version>1.1.0-SNAPSHOT</version>
+
+    <properties>
+        <module.ajsc.namespace.name>crud-api</module.ajsc.namespace.name>
+        <module.ajsc.namespace.version>v1</module.ajsc.namespace.version>
+        <ajscRuntimeVersion>2.0.0</ajscRuntimeVersion>
+        <absoluteDistFilesRoot>/appl/${project.artifactId}</absoluteDistFilesRoot>
+        <nexusproxy>https://nexus.onap.org</nexusproxy>
+
+        <!-- For NO Versioning, REMOVE the /${project.version} from the <distFilesRoot>
+            property, below. PLEASE, NOTE: If your ${project.version} is a "-SNAPSHOT"
+            version, THIS will be used as your directory structure. If you do NOT want
+            this, simply remove the "-SNAPSHOT" from your <version> declaration at the
+            top of pom.xml -->
+        <distFilesRoot>/appl/${project.artifactId}/${project.version}</distFilesRoot>
+        <runAjscHome>${basedir}/target/swm/package/nix/dist_files${distFilesRoot}</runAjscHome>
+
+        <!-- Port Selection. A value of 0 will allow for dynamic port selection.
+            For local testing, you may choose to hardcode this value to something like
+            8080 -->
+        <serverPort>0</serverPort>
+        <sslport>9520</sslport>
+
+        <testRouteOffer>workstation</testRouteOffer>
+        <testEnv>DEV</testEnv>
+        <checkstyle.config.location>google_checks.xml</checkstyle.config.location>
+
+        <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}/coverage-reports/jacoco.exec
+        </sonar.jacoco.reportPath>
+        <sonar.jacoco.reportMissing.force.zero>false</sonar.jacoco.reportMissing.force.zero>
+        <sonar.projectVersion>${project.version}</sonar.projectVersion>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>dom4j</groupId>
+            <artifactId>dom4j</artifactId>
+            <version>1.6.1</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.att.aft</groupId>
+            <artifactId>dme2</artifactId>
+            <version>3.1.200</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.4</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>2.6.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.thinkaurelius.titan</groupId>
+            <artifactId>titan-cassandra</artifactId>
+            <version>1.0.0</version>
+            <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>
+            <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>1.0.2</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-log4j12</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.google.guava</groupId>
+                    <artifactId>guava</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- Common logging framework -->
+        <dependency>
+          <groupId>org.openecomp.aai.logging-service</groupId>
+          <artifactId>common-logging</artifactId>
+          <version>1.0.0</version>
+        </dependency>
+
+        <dependency>
+          <groupId>ch.qos.logback</groupId>
+          <artifactId>logback-core</artifactId>
+          <version>1.1.7</version>
+        </dependency>
+
+        <dependency>
+          <groupId>org.onap.aai.aai-common</groupId>
+          <artifactId>aai-schema</artifactId>
+          <version>1.1.0-SNAPSHOT</version>
+        </dependency>
+
+        <dependency>
+          <groupId>org.onap.aai.aai-common</groupId>
+          <artifactId>aai-auth</artifactId>
+          <version>1.1.0-SNAPSHOT</version>
+        </dependency>
+
+        <dependency>
+          <groupId>org.eclipse.persistence</groupId>
+            <artifactId>eclipselink</artifactId>
+            <version>2.6.2</version>
+        </dependency>
+
+        <!-- Champ graph database library. -->
+        <dependency>
+            <groupId>org.openecomp.aai</groupId>
+            <artifactId>champ</artifactId>
+            <version>1.1.0-SNAPSHOT</version>
+            <scope>compile</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.hbase</groupId>
+                    <artifactId>hbase-client</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- Since for some reason the Champ library marks this artifact as an
+            'optional' dependency, we don't get it as a transitive dependency, even though
+            we are guaranteed No-Clas-Def errors if we don't have it on the class path,
+            so... we need to explicitly include it ourselves... -->
+        <dependency>
+            <groupId>com.thinkaurelius.titan</groupId>
+            <artifactId>titan-cassandra</artifactId>
+            <version>1.0.0</version>
+            <exclusions>
+              <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-log4j12</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.json</groupId>
+            <artifactId>json</artifactId>
+            <version>20131018</version>
+        </dependency>
+
+    </dependencies>
+
+
+    <repositories>
+      <repository>
+        <id>central</id>
+        <name>Maven 2 repository 2</name>
+        <url>http://repo2.maven.org/maven2/</url>
+      </repository>
+      <repository>
+        <id>ecomp-releases</id>
+        <name>ECOMP Release Repository</name>
+        <url>${nexusproxy}/content/repositories/releases/</url>
+      </repository>
+      <repository>
+        <id>ecomp-snapshots</id>
+        <name>ECOMP Snapshot Repository</name>
+        <url>${nexusproxy}/content/repositories/snapshots/</url>
+      </repository>
+      <repository>
+        <id>ecomp-staging</id>
+        <name>ECOMP Staging Repository</name>
+        <url>${nexusproxy}/content/repositories/staging/</url>
+      </repository>
+
+    </repositories>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-resources-plugin</artifactId>
+                <version>2.7</version>
+                <executions>
+                    <execution>
+                        <id>copy-docker-file</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>copy-resources</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>target</outputDirectory>
+                            <overwrite>true</overwrite>
+                            <resources>
+                                <resource>
+                                    <directory>${basedir}/src/main/docker</directory>
+                                    <filtering>true</filtering>
+                                    <includes>
+                                        <include>**/*</include>
+                                    </includes>
+                                </resource>
+                                <resource>
+                                  <directory>${basedir}/src/main/bin/</directory>
+                                </resource>
+                            </resources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+              <groupId>com.spotify</groupId>
+              <artifactId>docker-maven-plugin</artifactId>
+              <version>0.4.11</version>
+              <configuration>
+                <verbose>true</verbose>
+                <serverId>docker-hub</serverId>
+                <imageName>${docker.push.registry}/openecomp/${project.artifactId}</imageName>
+                <dockerDirectory>${docker.location}</dockerDirectory>
+                <imageTags>
+                  <imageTag>latest</imageTag>
+                </imageTags>
+                <forceTags>true</forceTags>
+              </configuration>
+            </plugin>
+
+            <!-- license plugin -->
+            <plugin>
+                <groupId>com.mycila</groupId>
+                <artifactId>license-maven-plugin</artifactId>
+                <version>3.0</version>
+                <configuration>
+                    <header>License.txt</header>
+                    <includes>
+                        <include>src/main/java/**</include>
+                    </includes>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>format</goal>
+                        </goals>
+                        <phase>process-sources</phase>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-site-plugin</artifactId>
+                <version>3.3</version>
+                <configuration>
+                    <reportPlugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-checkstyle-plugin</artifactId>
+                            <version>2.17</version>
+                            <reportSets>
+                                <reportSet>
+                                    <reports>
+                                      <report>checkstyle</report>
+                                    </reports>
+                                </reportSet>
+                            </reportSets>
+                        </plugin>
+                    </reportPlugins>
+                </configuration>
+            </plugin>
+
+            <plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-deploy-plugin</artifactId>
+              <configuration>
+                <skip>true</skip>
+              </configuration>
+            </plugin>
+
+            <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.7.201606060606</version>
+              <configuration>
+                <dumpOnExit>true</dumpOnExit>
+              </configuration>
+              <executions>
+                <execution>
+                  <id>jacoco-initialize-unit-tests</id>
+                  <goals>
+                    <goal>prepare-agent</goal>
+                  </goals>
+                  <configuration>
+                    <destFile>${project.build.directory}/coverage-reports/jacoco.exec
+                    </destFile>
+                    <!-- <append>true</append> -->
+                  </configuration>
+                </execution>
+              </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>runAjsc</id>
+            <build>
+                <defaultGoal>initialize</defaultGoal>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>exec-maven-plugin</artifactId>
+                        <version>1.3.2</version>
+                        <executions>
+                            <execution>
+                                <phase>initialize</phase>
+                                <goals>
+                                    <goal>java</goal>
+                                </goals>
+                                <configuration>
+                                    <includeProjectDependencies>false</includeProjectDependencies>
+                                    <includePluginDependencies>true</includePluginDependencies>
+                                    <executable>java</executable>
+                                    <mainClass>com.att.ajsc.runner.Runner</mainClass>
+                                    <executableDependency>
+                                        <groupId>com.att.ajsc</groupId>
+                                        <artifactId>ajsc-runner</artifactId>
+                                    </executableDependency>
+                                    <additionalClasspathElements>
+                                        <additionalClasspathElement>
+                                            ${basedir}/ajsc-shared-config/etc
+                                        </additionalClasspathElement>
+                                    </additionalClasspathElements>
+
+                                    <environmentVariables>
+                                        <AJSC_HOME>${runAjscHome}</AJSC_HOME>
+                                    </environmentVariables>
+
+                                    <!-- Main AJSC System Properties below (necessary for proper startup) -->
+                                    <systemProperties>
+                                        <systemProperty>
+                                            <key>AJSC_HOME</key>
+                                            <value>${runAjscHome}</value>
+                                        </systemProperty>
+                                        <systemProperty>
+                                            <key>CONFIG_HOME</key>
+                                            <value>${basedir}/appconfig-local/</value>
+                                        </systemProperty>
+                                        <systemProperty>
+                                            <key>AJSC_CONF_HOME</key>
+                                            <value>${basedir}/bundleconfig-local</value>
+                                        </systemProperty>
+                                        <systemProperty>
+                                            <key>logback.configurationFile</key>
+                                            <value>${basedir}/ajsc-shared-config/etc/logback.xml
+                                            </value>
+                                        </systemProperty>
+                                        <systemProperty>
+                                            <key>AJSC_SHARED_CONFIG</key>
+                                            <value>${basedir}/ajsc-shared-config</value>
+                                        </systemProperty>
+
+                                        <sysproperty>
+                                            <key>AJSC_EXTERNAL_LIB_FOLDERS</key>
+                                            <value>${basedir}/target/commonLibs</value>
+                                        </sysproperty>
+                                        <sysproperty>
+                                            <key>AJSC_EXTERNAL_PROPERTIES_FOLDERS</key>
+                                            <value>${basedir}/ajsc-shared-config/etc</value>
+                                        </sysproperty>
+
+                                        <systemProperty>
+                                            <key>AJSC_SERVICE_NAMESPACE</key>
+                                            <value>${module.ajsc.namespace.name}</value>
+                                        </systemProperty>
+                                        <systemProperty>
+                                            <key>AJSC_SERVICE_VERSION</key>
+                                            <value>${module.ajsc.namespace.version}</value>
+                                        </systemProperty>
+                                        <systemProperty>
+                                            <key>SOACLOUD_SERVICE_VERSION</key>
+                                            <value>${project.version}</value>
+                                        </systemProperty>
+                                        <systemProperty>
+                                            <key>server.port</key>
+                                            <value>${serverPort}</value>
+                                        </systemProperty>
+                                    </systemProperties>
+
+                                    <!-- Command Line Arguments to add to the java command. Here, you
+                                        can specify the port as well as the Context you want your service to run
+                                        in. Use context=/ to run in an unnamed Context (Root Context). The default
+                                        configuration of the AJSC is to run under the / Context. Setting the port
+                                        here can aid during the development phase of your service. However, you can
+                                        leave this argument out entirely, and the AJSC will default to using an Ephemeral
+                                        port. -->
+                                    <arguments>
+                                        <argument>context=/</argument>
+                                        <argument>port=${serverPort}</argument>
+                                        <argument>sslport=${sslport}</argument>
+                                    </arguments>
+                                </configuration>
+                            </execution>
+                        </executions>
+                        <configuration>
+                            <executable>java</executable>
+                        </configuration>
+                        <dependencies>
+                            <dependency>
+                                <groupId>com.att.ajsc</groupId>
+                                <artifactId>ajsc-runner</artifactId>
+                                <version>${ajscRuntimeVersion}</version>
+                            </dependency>
+                        </dependencies>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.apache.httpcomponents</groupId>
+                <artifactId>httpclient</artifactId>
+                <version>4.5</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.httpcomponents</groupId>
+                <artifactId>httpcore</artifactId>
+                <version>4.4.1</version>
+            </dependency>
+            <dependency>
+                <groupId>org.json</groupId>
+                <artifactId>json</artifactId>
+                <version>20131018</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <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-javadoc</id>
+        <url>dav:https://ecomp-nexus:8443/repository/aai/gizmo-javadoc/${project.version}</url>
+      </site>
+    </distributionManagement>
+</project>
diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/conf/jaxrsBeans.groovy b/src/main/ajsc/crud-api_v1/crud-api/v1/conf/jaxrsBeans.groovy
new file mode 100644 (file)
index 0000000..8462e3e
--- /dev/null
@@ -0,0 +1,11 @@
+beans{
+       xmlns cxf: "http://camel.apache.org/schema/cxf"
+       xmlns jaxrs: "http://cxf.apache.org/jaxrs"
+       xmlns util: "http://www.springframework.org/schema/util"
+       
+       echoService(org.openecomp.crud.service.JaxrsEchoService)
+       
+       util.list(id: 'jaxrsServices') {
+               ref(bean:'echoService')
+       }
+}
\ No newline at end of file
diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/docs/README.txt b/src/main/ajsc/crud-api_v1/crud-api/v1/docs/README.txt
new file mode 100644 (file)
index 0000000..3707179
--- /dev/null
@@ -0,0 +1 @@
+Place any docs here that you want to access within the ajsc upon deployment of your service.
diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/lib/README.txt b/src/main/ajsc/crud-api_v1/crud-api/v1/lib/README.txt
new file mode 100644 (file)
index 0000000..639e21b
--- /dev/null
@@ -0,0 +1 @@
+3rd party JAR's needed by your jars (if any) for a ajsc deployment package go here...
\ No newline at end of file
diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/props/module.props b/src/main/ajsc/crud-api_v1/crud-api/v1/props/module.props
new file mode 100644 (file)
index 0000000..17ebc08
--- /dev/null
@@ -0,0 +1 @@
+EXAMPLE.PROPERTY=EXAMLE_VALUE
\ No newline at end of file
diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/routes/crud.route b/src/main/ajsc/crud-api_v1/crud-api/v1/routes/crud.route
new file mode 100644 (file)
index 0000000..185aa6a
--- /dev/null
@@ -0,0 +1,4 @@
+<route xmlns="http://camel.apache.org/schema/spring" trace="true">
+  <from uri="att-dme2-servlet:///inventory?matchOnUriPrefix=true" />
+  <to uri="cxfbean:crudServices" />
+</route>
\ No newline at end of file
diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/routes/helloWorld.route b/src/main/ajsc/crud-api_v1/crud-api/v1/routes/helloWorld.route
new file mode 100644 (file)
index 0000000..bc3e178
--- /dev/null
@@ -0,0 +1,4 @@
+<route xmlns="http://camel.apache.org/schema/spring" trace="true">
+    <from uri="restlet:/__module_ajsc_namespace_name__/__module_ajsc_namespace_version__/helloWorld"/>
+    <to uri="bean:helloWorld?method=speak"/>
+</route>
\ No newline at end of file
diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/routes/jaxrsExample.route b/src/main/ajsc/crud-api_v1/crud-api/v1/routes/jaxrsExample.route
new file mode 100644 (file)
index 0000000..5158e4f
--- /dev/null
@@ -0,0 +1,5 @@
+<route xmlns="http://camel.apache.org/schema/spring" trace="true">
+  <from uri="att-dme2-servlet:///__module_ajsc_namespace_name__/__module_ajsc_namespace_version__/echo-service/?matchOnUriPrefix=true" />
+  <to uri="cxfbean:jaxrsServices" />
+</route>
+
diff --git a/src/main/assemble/ajsc_module_assembly.xml b/src/main/assemble/ajsc_module_assembly.xml
new file mode 100644 (file)
index 0000000..359f792
--- /dev/null
@@ -0,0 +1,69 @@
+<!-- 
+ Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+-->
+<assembly
+       xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+       <id>${version}</id>
+       <includeBaseDirectory>false</includeBaseDirectory>
+       <formats>
+               <format>zip</format>
+       </formats>
+       <fileSets>
+               <fileSet>
+                       <directory>${project.basedir}/target/versioned-ajsc/routes/</directory>
+                       <outputDirectory>${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/routes/</outputDirectory>
+                       <includes>
+                               <include>*.route</include>
+                       </includes>
+
+               </fileSet>
+
+               <fileSet>
+                       <directory>${project.basedir}/target/versioned-ajsc/docs/</directory>
+                       <outputDirectory>${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/docs/</outputDirectory>
+                       <includes>
+                               <include>*.*</include>
+                               <!-- <include>*.vm</include>  -->
+                       </includes>
+
+               </fileSet>
+
+               <fileSet>
+                       <directory>${project.basedir}/target/versioned-ajsc/lib/</directory>
+                       <outputDirectory>${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/lib/</outputDirectory>
+                       <includes>
+                               <include>*.jar</include>
+                       </includes>
+
+               </fileSet>
+               <fileSet>
+                       <directory>${project.basedir}/target/versioned-ajsc/extJars/</directory>
+                       <outputDirectory>${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/extJars/</outputDirectory>
+                       <includes>
+                               <include>*.jar</include>
+                       </includes>
+               </fileSet>
+               
+               <!-- also try to grab outputs from the "jar" plugin's package phase -->
+               <fileSet>
+                       <directory>${project.basedir}/target/</directory>
+                       <outputDirectory>${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/lib/</outputDirectory>
+                       <includes>
+                               <include>*.jar</include>
+                       </includes>
+               </fileSet>
+
+               <fileSet>
+                       <directory>${project.basedir}/target/versioned-ajsc/conf/</directory>
+                       <outputDirectory>${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/conf/</outputDirectory>
+                       <includes>
+                               <include>*.*</include>
+                       </includes>
+
+               </fileSet>
+       </fileSets>
+
+</assembly>
+
diff --git a/src/main/assemble/ajsc_props_assembly.xml b/src/main/assemble/ajsc_props_assembly.xml
new file mode 100644 (file)
index 0000000..6ee4093
--- /dev/null
@@ -0,0 +1,26 @@
+<!-- 
+ Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+-->
+<assembly
+       xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+       <id>${version}_properties</id>
+       <includeBaseDirectory>false</includeBaseDirectory>
+       <formats>
+               <format>zip</format>
+       </formats>
+       <fileSets>
+               <fileSet>
+                       <directory>${project.basedir}/target/versioned-ajsc/props</directory>
+                       <outputDirectory>${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/props/</outputDirectory>
+                       <includes>
+                               <include>*.props</include>
+                       </includes>
+
+               </fileSet>
+
+       </fileSets>
+
+</assembly>
+
diff --git a/src/main/assemble/ajsc_runtime_assembly.xml b/src/main/assemble/ajsc_runtime_assembly.xml
new file mode 100644 (file)
index 0000000..c86d265
--- /dev/null
@@ -0,0 +1,47 @@
+<!-- 
+ Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+-->
+<assembly
+       xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+       <id>runtimeEnvironment</id>
+       <includeBaseDirectory>false</includeBaseDirectory>
+       <formats>
+               <format>zip</format>
+       </formats>
+       <fileSets>
+               <fileSet>
+                       <directory>${project.basedir}/target/versioned-runtime/context/</directory>
+                       <outputDirectory>runtime/context/</outputDirectory>
+                       <includes>
+                               <include>*.context</include>
+                       </includes>
+               </fileSet>
+               <fileSet>
+                       <directory>${project.basedir}/target/versioned-runtime/serviceProperties/</directory>
+                       <outputDirectory>runtime/serviceProperties/</outputDirectory>
+                       <includes>
+                               <include>*.props</include>
+                       </includes>
+               </fileSet><fileSet>
+                       <directory>${project.basedir}/target/versioned-runtime/shiroRole</directory>
+                       <outputDirectory>runtime/shiroRole/</outputDirectory>
+                       <includes>
+                               <include>*.json</include>
+                       </includes>
+               </fileSet><fileSet>
+                       <directory>${project.basedir}/target/versioned-runtime/shiroUser</directory>
+                       <outputDirectory>runtime/shiroUser/</outputDirectory>
+                       <includes>
+                               <include>*.json</include>
+                       </includes>
+               </fileSet><fileSet>
+                       <directory>${project.basedir}/target/versioned-runtime/shiroUserRole</directory>
+                       <outputDirectory>runtime/shiroUserRole</outputDirectory>
+                       <includes>
+                               <include>*.json</include>
+                       </includes>
+               </fileSet>
+       </fileSets>
+</assembly>
\ No newline at end of file
diff --git a/src/main/bin/start.sh b/src/main/bin/start.sh
new file mode 100644 (file)
index 0000000..8a68460
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+BASEDIR="/opt/app/crud-api/"
+AJSC_HOME="$BASEDIR"
+AJSC_CONF_HOME="$AJSC_HOME/bundleconfig/"
+
+if [ -z "$CONFIG_HOME" ]; then
+       echo "CONFIG_HOME must be set in order to start up process"
+       exit 1
+fi
+
+if [ -z "$KEY_STORE_PASSWORD" ]; then
+       echo "KEY_STORE_PASSWORD must be set in order to start up process"
+       exit 1
+else
+       echo -e "KEY_STORE_PASSWORD=$KEY_STORE_PASSWORD\n" >> $AJSC_CONF_HOME/etc/sysprops/sys-props.properties
+fi
+
+if [ -z "$KEY_MANAGER_PASSWORD" ]; then
+       echo "KEY_MANAGER_PASSWORD must be set in order to start up process"
+       exit 1
+else
+       echo -e "KEY_MANAGER_PASSWORD=$KEY_MANAGER_PASSWORD\n" >> $AJSC_CONF_HOME/etc/sysprops/sys-props.properties
+fi
+
+# Add any spring bean configuration files to the Gizmo deployment
+if [ -n "$SERVICE_BEANS" ]; then
+        echo "Adding the following dynamic service beans to the deployment: "
+        mkdir -p /tmp/crud-api/v1/conf
+        for f in `ls $SERVICE_BEANS`
+        do
+                cp $SERVICE_BEANS/$f /tmp/crud-api/v1/conf
+                echo "Adding dynamic service bean $SERVICE_BEANS/$f"
+        done
+        jar uf /opt/app/crud-api/services/crud-api_v1.zip* -C /tmp/ crud-api
+        rm -rf /tmp/crud-api
+fi
+
+CLASSPATH="$AJSC_HOME/lib/*"
+CLASSPATH="$CLASSPATH:$AJSC_HOME/extJars/"
+CLASSPATH="$CLASSPATH:$AJSC_HOME/etc/"
+PROPS="-DAJSC_HOME=$AJSC_HOME"
+PROPS="$PROPS -DAJSC_CONF_HOME=$BASEDIR/bundleconfig/"
+PROPS="$PROPS -Dlogback.configurationFile=$BASEDIR/bundleconfig/etc/logback.xml"
+PROPS="$PROPS -DAJSC_SHARED_CONFIG=$AJSC_CONF_HOME"
+PROPS="$PROPS -DAJSC_SERVICE_NAMESPACE=crud-api"
+PROPS="$PROPS -DAJSC_SERVICE_VERSION=v1"
+PROPS="$PROPS -Dserver.port=9520"
+PROPS="$PROPS -DCONFIG_HOME=$CONFIG_HOME"
+JVM_MAX_HEAP=${MAX_HEAP:-1024}
+
+echo $CLASSPATH
+
+exec java -Xmx${JVM_MAX_HEAP}m $PROPS -classpath $CLASSPATH com.att.ajsc.runner.Runner context=// sslport=9520
diff --git a/src/main/config/ajsc-chef.jks b/src/main/config/ajsc-chef.jks
new file mode 100644 (file)
index 0000000..aeca770
Binary files /dev/null and b/src/main/config/ajsc-chef.jks differ
diff --git a/src/main/config/ajsc-jetty.xml b/src/main/config/ajsc-jetty.xml
new file mode 100644 (file)
index 0000000..17814f8
--- /dev/null
@@ -0,0 +1,114 @@
+<?xml version="1.0"  encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!-- Copyright (c) 2016 AT&T Intellectual Property. All rights reserved. -->
+<Configure id="ajsc-server" class="org.eclipse.jetty.server.Server">
+       <!-- DO NOT REMOVE!!!! This is setting up the AJSC Context -->
+       <New id="ajscContext" class="org.eclipse.jetty.webapp.WebAppContext">
+               <Set name="contextPath"><SystemProperty name="AJSC_CONTEXT_PATH" /></Set>
+               <Set name="extractWAR">true</Set>
+               <Set name="tempDirectory"><SystemProperty name="AJSC_TEMP_DIR" /></Set>
+               <Set name="war"><SystemProperty name="AJSC_WAR_PATH" /></Set>
+               <Set name="descriptor"><SystemProperty name="AJSC_HOME" />/etc/runner-web.xml</Set>
+               <Set name="overrideDescriptor"><SystemProperty name="AJSC_HOME" />/etc/ajsc-override-web.xml</Set>
+               <Set name="throwUnavailableOnStartupException">true</Set>
+               <Set name="servletHandler">
+                       <New class="org.eclipse.jetty.servlet.ServletHandler">
+                               <Set name="startWithUnavailable">false</Set>
+                       </New>
+               </Set>
+               <Set name="extraClasspath"><SystemProperty name="AJSC_HOME" />/extJars/json-20131018.jar</Set>
+       </New>
+
+       <Set name="handler">
+               <New id="Contexts"
+                       class="org.eclipse.jetty.server.handler.ContextHandlerCollection">
+                       <Set name="Handlers">
+                               <Array type="org.eclipse.jetty.webapp.WebAppContext">
+                                       <Item>
+                                               <Ref refid="ajscContext" />
+                                       </Item>
+                               </Array>
+                       </Set>
+               </New>
+       </Set>
+
+       <Call name="addBean">
+               <Arg>
+                       <New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
+                               <Set name="contexts">
+                                       <Ref refid="Contexts" />
+                               </Set>
+                               <Call id="extAppHotDeployProvider" name="addAppProvider">
+                                       <Arg>
+                                               <New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
+                                                       <Set name="monitoredDirName"><SystemProperty name="AJSC_HOME" />/extApps</Set>
+                                                       <Set name="scanInterval">10</Set>
+                                                       <Set name="extractWars">true</Set>
+                                               </New>
+                                       </Arg>
+                               </Call>
+                       </New>
+               </Arg>
+       </Call>
+
+       <New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
+               <Set name="keyStorePath">file:<SystemProperty name="CONFIG_HOME" />/auth/tomcat_keystore</Set>
+               <Set name="KeyStorePassword">
+                       <Call class="org.eclipse.jetty.util.security.Password" name="deobfuscate">
+                               <Arg><SystemProperty name="KEY_STORE_PASSWORD" /></Arg>
+                       </Call>
+               </Set>
+               <Set name="KeyManagerPassword">
+                       <Call class="org.eclipse.jetty.util.security.Password" name="deobfuscate">
+                               <Arg><SystemProperty name="KEY_MANAGER_PASSWORD" /></Arg>
+                       </Call>
+               </Set>
+               <Set name="needClientAuth">true</Set>
+               <Set name="wantClientAuth">true</Set>
+       </New>
+
+       <Call id="sslConnector" name="addConnector">
+               <Arg>
+                       <New class="org.eclipse.jetty.server.ServerConnector">
+                               <Arg name="server">
+                                       <Ref refid="ajsc-server" />
+                               </Arg>
+                               <Arg name="factories">
+                                       <Array type="org.eclipse.jetty.server.ConnectionFactory">
+                                               <Item>
+                                                       <New class="org.eclipse.jetty.server.SslConnectionFactory">
+                                                               <Arg name="next">http/1.1</Arg>
+                                                               <Arg name="sslContextFactory">
+                                                                       <Ref refid="sslContextFactory" />
+                                                               </Arg>
+                                                       </New>
+                                               </Item>
+                                               <Item>
+                                                       <New class="org.eclipse.jetty.server.HttpConnectionFactory">
+                                                               <Arg name="config">
+                                                                       <New class="org.eclipse.jetty.server.HttpConfiguration">
+                                                                               <Call name="addCustomizer">
+                                                                                       <Arg>
+                                                                                               <New class="org.eclipse.jetty.server.SecureRequestCustomizer" />
+                                                                                       </Arg>
+                                                                               </Call>
+                                                                       </New>
+                                                               </Arg>
+                                                       </New>
+                                               </Item>
+                                       </Array>
+                               </Arg>
+                               <Set name="port"><SystemProperty name="AJSC_HTTPS_PORT" default="9520" /></Set>
+                               <Set name="idleTimeout">30000</Set>
+                       </New>
+               </Arg>
+       </Call>
+
+       <Get name="ThreadPool">
+               <Set name="minThreads"><SystemProperty name="AJSC_JETTY_ThreadCount_MIN" /></Set>
+               <Set name="maxThreads"><SystemProperty name="AJSC_JETTY_ThreadCount_MAX" /></Set>
+               <Set name="idleTimeout"><SystemProperty name="AJSC_JETTY_IDLETIME_MAX" /></Set>
+               <Set name="detailedDump">false</Set>
+       </Get>
+
+</Configure>
diff --git a/src/main/config/ajsc-override-web.xml b/src/main/config/ajsc-override-web.xml
new file mode 100644 (file)
index 0000000..84a7920
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- 
+ Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+-->
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+       metadata-complete="false" version="3.0">
+
+       <filter-mapping>
+               <filter-name>InterceptorFilter</filter-name>
+               <url-pattern>/services/*</url-pattern>
+       </filter-mapping> 
+       <filter-mapping>
+               <filter-name>InterceptorFilter</filter-name>
+               <url-pattern>/rest/*</url-pattern>
+       </filter-mapping>  
+
+       <filter-mapping>
+               <filter-name>springSecurityFilterChain</filter-name>
+               <url-pattern>/*</url-pattern>
+       </filter-mapping>
+               
+       <servlet-mapping>
+               <servlet-name>ManagementServlet</servlet-name>
+               <url-pattern>/mgmt</url-pattern>
+       </servlet-mapping>
+        
+        <servlet-mapping>
+                       <servlet-name>RestletServlet</servlet-name>
+                       <url-pattern>/rest/*</url-pattern>
+       </servlet-mapping>
+
+       <servlet-mapping>
+               <servlet-name>CamelServlet</servlet-name>
+               <url-pattern>/services/*</url-pattern>
+       </servlet-mapping>
+
+       <servlet-mapping>
+               <servlet-name>jsp</servlet-name>
+               <url-pattern>*.jsp</url-pattern>
+               <url-pattern>*.jspf</url-pattern>
+               <url-pattern>*.jspx</url-pattern>
+               <url-pattern>*.xsp</url-pattern>
+               <url-pattern>*.JSP</url-pattern>
+               <url-pattern>*.JSPF</url-pattern>
+               <url-pattern>*.JSPX</url-pattern>
+               <url-pattern>*.XSP</url-pattern>
+       </servlet-mapping>
+       <servlet-mapping>
+               <servlet-name>default</servlet-name>
+               <url-pattern>/*</url-pattern>
+       </servlet-mapping>
+</web-app>
\ No newline at end of file
diff --git a/src/main/config/ajscJetty.jks b/src/main/config/ajscJetty.jks
new file mode 100644 (file)
index 0000000..48cdbff
Binary files /dev/null and b/src/main/config/ajscJetty.jks differ
diff --git a/src/main/config/cadi.properties b/src/main/config/cadi.properties
new file mode 100644 (file)
index 0000000..4720e1c
--- /dev/null
@@ -0,0 +1,36 @@
+#This properties file is used for defining AAF properties related to the CADI framework. This file is used for running AAF framework
+
+#In order to test functionality of cadi-ajsc-plugin locally cross domain cookie. Cadi "should" find your hostname for you. 
+#However, we have seen some situations where this fails. A Local testing
+#modification can include modifying your hosts file so that you can use "mywebserver.att.com" for your localhost in order
+#to test/verify GLO functionality locally. If you are on a Windows machine, you will already have a machine name associated with 
+#it that will utilize an AT&T domain such as "sbc.com". You may need to add your  domain to this as a comma separated list depending
+#upon your particular machine domain. This property is commented out as cadi SHOULD find your machine name. With version 1.2.1 of cadi, 
+#it appears to resolve Mac machine names as well, now. But, this can be somewhat inconsistent depending on your specific working envrironment.
+hostname=mywebserver.att.com
+
+#Setting csp_domain to PROD will allow for testing using your attuid and password through GLO.
+csp_domain=PROD
+csp_devl_localhost=true
+
+basic_realm=csp.att.com
+#basic_realm=aaf.att.com
+basic_warn=TRUE
+
+cadi_loglevel=WARN
+cadi_keyfile=target/swm/package/nix/dist_files/appl/crud-api/etc/keyfile
+
+# Configure AAF
+#These are dummy values add appropriate values required
+aaf_url=url
+
+#AJSC - MECHID
+#These are dummy values add appropriate values required
+aaf_id=dummyid@ajsc.att.com
+aaf_password=enc:277edqJCjT0RlUI3BtbDQa-3Ha-CQGd
+aaf_timeout=5000
+aaf_clean_interval=30000
+aaf_user_expires=5000
+aaf_high_count=1000
+
+
diff --git a/src/main/config/jul-redirect.properties b/src/main/config/jul-redirect.properties
new file mode 100644 (file)
index 0000000..8b6624d
--- /dev/null
@@ -0,0 +1,13 @@
+
+#      Bridge JUL->slf4j Logging Configuration File
+#
+# This file bridges the JUL logging infrastructure into
+# SLF4J so JUL logs go to logback implementation provided
+# in this project.  SLF4J also captures log4j and has 
+# other framework options as well providing a common
+# logging infrastructure for capturing all logs from different
+# libraries using different frameworks in one place.
+
+#      Global properties
+handlers=org.slf4j.bridge.SLF4JBridgeHandler
+.level= ALL
diff --git a/src/main/config/keyfile b/src/main/config/keyfile
new file mode 100644 (file)
index 0000000..882e86a
--- /dev/null
@@ -0,0 +1,27 @@
+ZuIwp0TkyVPDeX1Up-8JtkMWvjsCpoiu1_VKeWrtrvxunvAke8_tiFyHPPyb2nkhepFYj6tXzpfS
+rGz5XF_TH9NbsKaP8u0HV5clz2WriYQRvHS85vjY7hXxkpFuLb7zkLAPqTyIDpj7FiW61NzsRUAq
+TM8jH16jr7mBNnb56w24mNGOwznMPcIZKcjgZU1ekaPDFpWyhQElU7Y0q_94P_Gkk45r66Hj22sU
+OiOaaftmudZlswLw8-8Zaakqf2yW9HjMVfuYCwSodBHCW5rdB3Ctb5W36rnD_AQco3Ky2PgPmqvk
+QkJYuUHpbuDqVHqLOajlKSIGMTIqAIBg51fRaaONtD-Q5xzY8E5wO1YWTLKcP5tsNvUpzM8Wu3NS
+ynpGpUcvlTqWWsGzTbzOyamyKkdNdx97sSqjM25Zh1-ps48h6cddGYWpab7SUvqRCS11QBUyLTry
+2iwTEHMhHRIbo7PO99ALQfuq9gI1zKGfurJdvLBeBaFs5SCF0AiCZ3WcDO8Rv3HpxVZ2_ShbDxb0
+eMoO6SotXu51fj8Y3-WqsfZziQyEsHyqpg5uQ6yUtz01h5YHLEoVuotF1U4agmQR6kEkYk-wNOiZ
+v-8gaA9gtbLoAdKhuKFxQgQLNMf6GzVzZNujbmDzLoZAP_mXAv29aBPaf64Ugzv-Oa5GZdBgD-Xd
+_pahML-ionw99r0TnkpShYmDqMKhMdjaP3m87WIAZkIB-L-VTyKcEsJ4340VSzCOsv3waiM0S89u
+4cMcG5y-PLY8IoipIlLUPTWD3SjcQ9DV1Dt3T5KjdWLsj48D3W4K4e9PB8yxs0gtUjgVUR2_xEir
+G5eDO9Ac1eHFWGDFFP0SgG-TbHJUKlvy9mwLzmU0fC3xPjhqmIr-v0HxF7HN-tmb1LHDorno8tSN
+u7kUGcKSchIiFfvkd066crUb2mH7PnXTaWmAjyVj9VsBExFUYEdpHMAV4sAP9-RxZGDRt46UhrDK
+QZvvNhBVyOEjHPHWI4vl1r1v8HNH1_2jZu5DVJWyHWR56aCo1lhFH9_X6UAHUHbnXViDONZOVXlT
+9-WD0tk2zJGuwrhdZDAnPnAmjfwbwbpnr5Hmex1i1JiD7WVyP1kbfoej2TmdiYbxr9oBYaGQ29JI
+aHod7MQCLtvL1z5XgnDPLZ4y3_9SbqHKYbNa8UgZkTLF5EacGThYVFDLA9cbafHDtR1kMGE3vv4D
+EJ-0pAYTOGmKlVI7DwNyKsY9JTyudrxTqhOxi9jgcJNWiUaNe9yhL8Pyc2YBqUTTYhh_a2d1rvkZ
+0Gh1crviVxqBrIkRKaMRXZ4f1vDLz-3NvG_vwPOo8WRFo5nGmSdTw7CjBaigJ_cYCfDhoP11pEnw
+cndsZNcHs-v05LlxeIIMDD_f5Bvz-il_DLA4eK2HqgLdxh8ziSDl2azk14MJY4amzz6reEXUuKLV
+RsZGf_jbDGKhE2HuDQ5ovoLOi4OqE1oRuqh-dGxitrYouP2SN1l_1tCEMRth86FMV-6AQtZsvdUo
+y9MtQ7e35atjA8nHtgADlDTmJBKQiUHUsOZ77p1qp17HAFMovUkc739opfEYnKUn6Itpw5Ipm_Is
+ra6chJUfMpOFof5rb5OjqFAN27c_-mPo1lQU3ndYlKGh_n5V8ufX6v2Yri8WzOPf6hjVYotkmoMP
+NPAICDCB8W5ddBjsopzLVVEtaXDu9Qj6-zf77hT4iQ7rBd2Ner8iLqN3Kis0dvkNM3_uH8onau1G
+Y_YYw7PPSZyd2S_7Dd6G-IG4ayO6e5DD6oUwwekyiQI_3rTXNa_wldGxqW9u818010ekE4Qdlfcj
+beIn7fAeaOjReZ87hRgWyMs-EgTVHw8RL3yI_O6VvRTVRONRF1Y4C_-IYa8z-bfrwXx3BBd9TTgb
+EnS9wVOyC2OgUN6BhPLGLhxzkJ05nEjizXEc9t5EPYoSRwesajGGrrG_0-qWbuU5hKLPLkyeJLHb
+5HXOTVsrUR59Vov2M3_EswkxcImblox3k3VS2yihZMGyfqLzZIUXgd8ufkevKKU6DxwacGTb
\ No newline at end of file
diff --git a/src/main/config/runner-web.xml b/src/main/config/runner-web.xml
new file mode 100644 (file)
index 0000000..b51aff4
--- /dev/null
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- 
+ Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+-->
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+       metadata-complete="false" version="3.0">
+
+       <context-param>
+               <param-name>contextConfigLocation</param-name>
+               <param-value>/WEB-INF/spring-servlet.xml,
+                                       classpath:applicationContext.xml
+               </param-value>
+       </context-param>
+       
+       <context-param>
+        <param-name>spring.profiles.default</param-name>
+        <param-value>nooauth</param-value>
+    </context-param>
+    
+       <listener>
+               <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+       </listener>
+       
+       <servlet>
+               <servlet-name>ManagementServlet</servlet-name>
+               <servlet-class>ajsc.ManagementServlet</servlet-class>
+       </servlet>
+
+    
+       <filter> 
+               <filter-name>InterceptorFilter</filter-name>
+               <filter-class>ajsc.filters.InterceptorFilter</filter-class>
+               <init-param>
+                <param-name>preProcessor_interceptor_config_file</param-name>
+                <param-value>/etc/PreProcessorInterceptors.properties</param-value>
+        </init-param>
+        <init-param>
+                <param-name>postProcessor_interceptor_config_file</param-name>
+                <param-value>/etc/PostProcessorInterceptors.properties</param-value>
+        </init-param>
+        
+       </filter>
+
+        <servlet>
+               <servlet-name>RestletServlet</servlet-name>
+               <servlet-class>ajsc.restlet.RestletSpringServlet</servlet-class>
+               <init-param>
+                               <param-name>org.restlet.component</param-name>
+                               <param-value>restletComponent</param-value>
+               </init-param>
+       </servlet>
+       
+       <servlet>
+               <servlet-name>CamelServlet</servlet-name>
+               <servlet-class>ajsc.servlet.AjscCamelServlet</servlet-class>
+       </servlet>
+
+
+       <filter>
+               <filter-name>springSecurityFilterChain</filter-name>
+               <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
+       </filter>
+
+       <servlet>
+               <servlet-name>spring</servlet-name>
+               <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+               <load-on-startup>1</load-on-startup>
+       </servlet>      
+       
+<!--   <servlet-mapping>
+               <servlet-name>spring</servlet-name>
+               <url-pattern>/</url-pattern>
+       </servlet-mapping>-->
+
+<!-- BEGIN jsp -->
+
+  <servlet id="jsp">
+    <servlet-name>jsp</servlet-name>
+    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
+  </servlet>
+
+       
+       
+
+         
+       <!-- BEGIN static content -->
+       <servlet>
+          <servlet-name>default</servlet-name>
+           <servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
+           <init-param>
+               <param-name>dirAllowed</param-name>
+               <param-value>true</param-value>
+           </init-param>
+       </servlet>
+       <!-- END static content -->     
+</web-app>
diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile
new file mode 100644 (file)
index 0000000..e4459d1
--- /dev/null
@@ -0,0 +1,26 @@
+FROM ubuntu:14.04
+
+ARG MICRO_HOME=/opt/app/crud-api
+ARG BIN_HOME=$MICRO_HOME/bin
+
+RUN apt-get update
+
+# Install and setup java8
+RUN apt-get update && apt-get install -y software-properties-common
+## sudo -E is required to preserve the environment. If you remove that line, it will most like freeze at this step
+RUN sudo -E add-apt-repository ppa:openjdk-r/ppa && apt-get update && apt-get install -y openjdk-8-jdk
+## Setup JAVA_HOME, this is useful for docker commandline
+ENV JAVA_HOME usr/lib/jvm/java-8-openjdk-amd64
+RUN export JAVA_HOME
+
+# Build up the deployment folder structure
+RUN mkdir -p $MICRO_HOME
+ADD swm/package/nix/dist_files/appl/crud-api/* $MICRO_HOME/
+RUN mkdir -p $BIN_HOME
+COPY *.sh $BIN_HOME
+RUN chmod 755 $BIN_HOME/*
+RUN ln -s /logs $MICRO_HOME/logs
+
+EXPOSE 9520 9520
+
+CMD ["/opt/app/crud-api/bin/start.sh"]
diff --git a/src/main/java/org/openecomp/crud/dao/GraphDao.java b/src/main/java/org/openecomp/crud/dao/GraphDao.java
new file mode 100644 (file)
index 0000000..20b568c
--- /dev/null
@@ -0,0 +1,138 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.dao;
+
+import org.openecomp.crud.entity.Edge;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+
+import java.util.List;
+import java.util.Map;
+
+public interface GraphDao {
+
+  public Vertex getVertex(String id, String type) throws CrudException;
+
+  /**
+   * Retrieve all of the edges which are incident to the vertex with the specified identifier.
+   *
+   * @param id - The unique identifier of the vertex to retrieve the edges for.
+   * @return - A collection of edges.
+   * @throws CrudException
+   */
+  public List<Edge> getVertexEdges(String id) throws CrudException;
+
+  /**
+   * Retrieve a collection of {@link Vertex} objects which match the supplied type label
+   * and filter properties.
+   *
+   * @param type   - The vertex type that we want to retrieve.
+   * @param filter - The parameters to filter our results by.
+   * @return - A collection of vertices.
+   * @throws CrudException
+   */
+  public List<Vertex> getVertices(String type, Map<String, Object> filter) throws CrudException;
+
+  /**
+   * Retrieve an {@link Edge} from the graph database by specifying its unique identifier.
+   *
+   * @param id - The unique identifier for the Edge to be retrieved.
+   * @return - The Edge corresponding to the specified identifier.
+   * @throws CrudException
+   */
+  public Edge getEdge(String id, String type) throws CrudException;
+
+  /**
+   * Retrieve a collection of {@link Edge} objects with a given type and which match a set of
+   * supplied filter parameters.
+   *
+   * @param type   - The type of edges that we are interested in.
+   * @param filter - The parameters that we want to filter our edges by.
+   * @return - A collection of edges which match the supplied filter parameters.
+   * @throws CrudException
+   */
+  public List<Edge> getEdges(String type, Map<String, Object> filter) throws CrudException;
+
+  /**
+   * Insert a new {@link Vertex} into the graph data store.
+   *
+   * @param type       - The type label to assign to the vertex.
+   * @param properties - The properties to associated with this vertex.
+   * @return - The {@link Vertex} object that was created.
+   * @throws CrudException
+   */
+  public Vertex addVertex(String type, Map<String, Object> properties) throws CrudException;
+
+  /**
+   * Updates an existing {@link Vertex}.
+   *
+   * @param id         - The unique identifier of the vertex to be updated.
+   * @param properties - The properties to associate with the vertex.
+   * @return - The udpated vertex.
+   * @throws CrudException
+   */
+  public Vertex updateVertex(String id, String type, Map<String, Object> properties)
+      throws CrudException;
+
+  /**
+   * Removes the specified vertex from the graph data base.
+   *
+   * <p>NOTE: The vertex MUST contain NO incident edges before it can be deleted.
+   *
+   * @param id - The unique identifier of the vertex to be deleted.
+   * @throws CrudException
+   */
+  public void deleteVertex(String id, String type) throws CrudException;
+
+  /**
+   * Adds an edge to the graph database.
+   *
+   * @param type       - The 'type' label to apply to the edge.
+   * @param source     - The source vertex for this edge.
+   * @param target     - The target vertex for this edge.
+   * @param properties - The properties map to associate with this edge.
+   * @return - The {@link Edge} object that was created.
+   * @throws CrudException
+   */
+  public Edge addEdge(String type, Vertex source, Vertex target, Map<String, Object> properties)
+      throws CrudException;
+
+  /**
+   * Updates an existing {@link Edge}.
+   *
+   * @param id         - The unique identifier of the edge to be updated.
+   * @param properties - The properties to associate with the edge.
+   * @return - The update edge.
+   * @throws CrudException
+   */
+  public Edge updateEdge(Edge edge) throws CrudException;
+
+  /**
+   * Remove the specified edge from the graph data base.
+   *
+   * @param id - The unique identifier of the edge to be deleted.
+   * @throws CrudException
+   */
+  public void deleteEdge(String id, String type) throws CrudException;
+}
diff --git a/src/main/java/org/openecomp/crud/dao/champ/ChampDao.java b/src/main/java/org/openecomp/crud/dao/champ/ChampDao.java
new file mode 100644 (file)
index 0000000..ff0a332
--- /dev/null
@@ -0,0 +1,750 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.dao.champ;
+
+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.graph.impl.TitanChampGraphImpl;
+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.cl.api.Logger;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.crud.dao.GraphDao;
+import org.openecomp.crud.entity.Edge;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.logging.CrudServiceMsgs;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Random;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+
+/**
+ * This is the integration layer between the CRUD API service and the low level Champ library
+ * for graph database interaction.
+ */
+public class ChampDao implements GraphDao {
+
+  public static final String CONFIG_STORAGE_BACKEND = "storage.backend";
+  public static final String CONFIG_STORAGE_BACKEND_DB = "storage.backend.db";
+  public static final String STORAGE_HBASE_DB = "hbase";
+  public static final String STORAGE_CASSANDRA_DB = "cassandra";
+  public static final String CONFIG_STORAGE_HOSTNAMES = "storage.hostnames";
+  public static final String CONFIG_STORAGE_PORT = "storage.port";
+  public static final String CONFIG_HBASE_ZNODE_PARENT = "storage.hbase.ext.zookeeper.znode.parent";
+  public static final String CONFIG_GRAPH_NAME = "graph.name";
+  public static final String GRAPH_UNQ_INSTANCE_ID_SUFFIX = "graph.unique-instance-id-suffix";
+
+  public static final String CONFIG_EVENT_STREAM_PUBLISHER = "event.stream.publisher";
+  public static final String CONFIG_EVENT_STREAM_NUM_PUBLISHERS = "event.stream.num-publishers";
+
+
+  public static final String DEFAULT_GRAPH_NAME = "default_graph";
+
+  /**
+   * Set of configuration properties for the DAI.
+   */
+  private Properties daoConfig;
+
+  /**
+   * Instance of the API used for interacting with the Champ library.
+   */
+  private ChampGraph champApi = null;
+
+  private Logger logger = LoggerFactory.getInstance().getLogger(ChampDao.class.getName());
+
+
+  /**
+   * Creates a new instance of the ChampDao.
+   *
+   * @param config - Set of configuration properties to be applied to this instance
+   *               of the DAO.
+   */
+  public ChampDao(Properties config) {
+
+    // Store the configuration properties.
+    daoConfig = config;
+
+    // Apply the configuration to the DAO.
+    configure();
+
+  }
+
+
+  @Override
+  public Vertex getVertex(String id, String type) throws CrudException {
+
+    try {
+
+      if (logger.isDebugEnabled()) {
+        logger.debug("getVertex with id: " + id);
+      }
+
+      long idAsLong = Long.parseLong(id);
+
+      // Request the vertex from the graph db.
+      Optional<ChampObject> retrievedVertex = champApi.retrieveObject(idAsLong);
+
+      // Did we find it?
+      if (retrievedVertex.isPresent() && retrievedVertex.get().getProperties()
+          .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName())!=null && retrievedVertex.get().getProperties()
+          .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()
+          .equalsIgnoreCase(type)) {  
+        
+        // Yup, convert it to a Vector object and return it.
+        return vertexFromChampObject(retrievedVertex.get(),type);
+
+      } else {
+
+        // We didn't find a vertex with the supplied id, so just throw an
+        // exception.
+        throw new CrudException("No vertex with id " + id + " found in graph",
+            javax.ws.rs.core.Response.Status.NOT_FOUND);
+      }
+
+    } catch (ChampUnmarshallingException e) {
+
+      // Something went wrong - throw an exception.
+      throw new CrudException(e.getMessage(),
+          javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+    }
+  }
+
+
+  @Override
+  public List<Edge> getVertexEdges(String id) throws CrudException {
+
+    if (logger.isDebugEnabled()) {
+      logger.debug("get Edges incident to vertex with id: " + id + " from graph");
+    }
+
+    try {
+      long idAsLong = Long.parseLong(id); // GDF - what to do about id???
+
+      // Request the vertex from the graph db.
+      Optional<ChampObject> retrievedVertex = champApi.retrieveObject(idAsLong);
+
+      // Did we find it?
+      if (retrievedVertex.isPresent()) {
+
+        // Query the Champ library for the edges which are incident to the specified
+        // vertex.
+        Stream<ChampRelationship> relationships =
+            champApi.retrieveRelationships(retrievedVertex.get());
+
+        // Build an edge list from the result stream.
+        List<Edge> edges = new ArrayList<Edge>();
+        relationships.forEach(r -> edges.add(edgeFromChampRelationship(r)));
+
+        return edges;
+
+      } else {
+
+        // We couldn't find the specified vertex, so throw an exception.
+        throw new CrudException("No vertex with id " + id + " found in graph",
+            javax.ws.rs.core.Response.Status.NOT_FOUND);
+      }
+
+    } catch (ChampUnmarshallingException e) {
+
+      // Something went wrong, so throw an exception.
+      throw new CrudException(e.getMessage(),
+          javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+
+    } catch (ChampObjectNotExistsException e) {
+
+      // We couldn't find the specified vertex, so throw an exception.
+      throw new CrudException("No vertex with id " + id + " found in graph",
+          javax.ws.rs.core.Response.Status.NOT_FOUND);
+    }
+  }
+
+
+  @Override
+  public Vertex addVertex(String type, Map<String, Object> properties) throws CrudException {
+
+    if (logger.isDebugEnabled()) {
+      logger.debug("Add/update vertex: {label: " + type
+          + " properties:" + propertiesMapToString(properties));
+    }
+
+    //Add the aai_node_type so that AAI can read the data created by gizmo
+    properties.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
+
+    // Create an object to represent our vertex in the format expected by the Champ library.
+    ChampObject objectToCreate = buildChampObject(type, properties);
+
+    try {
+
+      // Ask the Champ library to store our vertex, placing the returned object into a
+      // list so that we can easily put that into our result object.
+      return vertexFromChampObject(champApi.storeObject(objectToCreate),type);
+
+    } catch (ChampMarshallingException
+        | ChampSchemaViolationException
+        | ChampObjectNotExistsException e) {
+
+      // Something went wrong - throw an exception.
+      throw new CrudException(e.getMessage(),
+          javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+    }
+  }
+
+
+  @Override
+  public Vertex updateVertex(String id, String type, Map<String, Object> properties)
+      throws CrudException {
+
+    if (logger.isDebugEnabled()) {
+      logger.debug("Update vertex with id: " + id + " with properties: "
+          + propertiesMapToString(properties));
+    }
+    //Add the aai_node_type so that AAI can read the data created by gizmo
+    properties.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
+
+    try {
+      // Now, build the updated version of the Champ Object...
+      ChampObject updateObject = buildChampObject(id, type, properties);
+      // ...and send it to the Champ library.
+      return vertexFromChampObject(champApi.replaceObject(updateObject),type);
+
+    } catch (ChampObjectNotExistsException e) {
+      throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND);
+    } catch (NumberFormatException | ChampMarshallingException | ChampSchemaViolationException e) {
+      throw new CrudException(e.getMessage(),
+          javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+    }
+
+  }
+
+
+  @Override
+  public List<Vertex> getVertices(String type, Map<String, Object> filter) throws CrudException {
+
+    if (logger.isDebugEnabled()) {
+      logger.debug("Retrieve vertices with type label: " + type + " which map query parameters: "
+          + propertiesMapToString(filter));
+    }
+
+
+    filter.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
+
+
+    Stream<ChampObject> retrievedVertices = champApi.queryObjects(filter);
+
+    List<Vertex> vertices = retrievedVertices
+        .map(v -> vertexFromChampObject(v,type))
+        .collect(Collectors.toList());
+
+
+    if (logger.isDebugEnabled()) {
+      logger.debug("Resulting vertex list: " + retrievedVertices);
+    }
+
+    // ...and return it to the caller.
+    return vertices;
+  }
+
+  private Object getRelKey(String id) {
+    Object key = id;
+    // convert into Long if applicable . TODO : revisit in story NUC-304
+    try {
+      key = Long.parseLong(id);
+    } catch (NumberFormatException e) {
+      // The id isn't a Long, leave it as a string
+    }
+
+    return key;
+  }
+
+  @Override
+  public Edge getEdge(String id, String type) throws CrudException {
+
+    if (logger.isDebugEnabled()) {
+      logger.debug("Get edge with id: " + id);
+    }
+
+    try {
+
+      // Request the edge from the graph db.
+      Optional<ChampRelationship> relationship = champApi.retrieveRelationship(getRelKey(id));
+
+      // Did we find it?
+      if (relationship.isPresent() && relationship.get().getType().equals(type)) {
+
+        // Yup - return the result.
+        return edgeFromChampRelationship(relationship.get());
+
+      } else {
+
+        // We didn't find an edge with the supplied id, so throw an exception.
+        throw new CrudException("No edge with id " + id + " found in graph",
+            javax.ws.rs.core.Response.Status.NOT_FOUND);
+      }
+
+    } catch (ChampUnmarshallingException e) {
+
+      // Something went wrong, so throw an exception.
+      throw new CrudException(e.getMessage(),
+          javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+    }
+  }
+
+  @Override
+  public Edge addEdge(String type,
+                      Vertex source,
+                      Vertex target,
+                      Map<String, Object> properties) throws CrudException {
+
+    // For now, assume source and target are straight ids...
+    try {
+
+      Optional<ChampObject> sourceObject
+          = champApi.retrieveObject(Long.parseLong(source.getId().get()));
+      if (!sourceObject.isPresent() || !sourceObject.get().getType().equals(source.getType())) {
+        throw new CrudException("Error creating edge - source vertex with id " + source
+            + " does not exist in graph data base",
+            javax.ws.rs.core.Response.Status.BAD_REQUEST);
+      }
+
+      Optional<ChampObject> targetObject
+          = champApi.retrieveObject(Long.parseLong(target.getId().get()));
+      if (!targetObject.isPresent() || !targetObject.get().getType().equals(target.getType())) {
+        throw new CrudException("Error creating edge - target vertex with id " + target
+            + " does not exist in graph data base",
+            javax.ws.rs.core.Response.Status.BAD_REQUEST);
+      }
+
+      // Now, create the ChampRelationship object for our edge and store it in
+      // the graph database.
+      return edgeFromChampRelationship(
+          champApi.storeRelationship(
+              new ChampRelationship.Builder(sourceObject.get(), targetObject.get(), type)
+                  .properties(properties)
+                  .build()));
+
+    } catch (ChampMarshallingException
+        | ChampObjectNotExistsException
+        | ChampSchemaViolationException
+        | ChampRelationshipNotExistsException
+        | ChampUnmarshallingException e) {
+
+      throw new CrudException("Error creating edge: " + e.getMessage(),
+          javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+    }
+  }
+
+
+  @Override
+  public List<Edge> getEdges(String type, Map<String, Object> filter) throws CrudException {
+
+    filter.put(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString(), type);
+
+    Stream<ChampRelationship> retrievedRelationships = champApi.queryRelationships(filter);
+    // Process the result stream from the Champ library into an Edge list, keeping only
+    // edges of the specified type.
+    List<Edge> edges = retrievedRelationships
+        .map(r -> edgeFromChampRelationship(r))
+        .collect(Collectors.toList());
+
+    return edges;
+  }
+
+  @Override
+  public Edge updateEdge(Edge edge) throws CrudException {
+
+    if (logger.isDebugEnabled()) {
+      logger.debug("Update edge with id: " + edge.getId() + " with properties: "
+          + propertiesMapToString(edge.getProperties()));
+    }
+
+    try {
+      // Now, build the updated version of the Champ Relationship...
+      ChampRelationship updateRelationship = new ChampRelationship.Builder(
+          buildChampObject(edge.getSource().getId().get(), edge.getSource().getType(),
+              edge.getSource().getProperties()),
+          buildChampObject(edge.getTarget().getId().get(), edge.getTarget().getType(),
+              edge.getTarget().getProperties()),
+          edge.getType()).key(getRelKey(edge.getId().get()))
+          .properties(edge.getProperties()).build();
+      // ...and send it to the Champ library.
+      return edgeFromChampRelationship(champApi.replaceRelationship(updateRelationship));
+
+
+    } catch (ChampRelationshipNotExistsException ex) {
+      throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND);
+    } catch (NumberFormatException | ChampUnmarshallingException | ChampMarshallingException
+        | ChampSchemaViolationException ex) {
+
+      throw new CrudException(ex.getMessage(),
+          javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+    }
+  }
+
+  @Override
+  public void deleteVertex(String id, String type) throws CrudException {
+
+    try {
+
+      // First, retrieve the vertex that we intend to delete.
+      Optional<ChampObject> retrievedVertex = champApi.retrieveObject(Long.parseLong(id));
+
+      // Did we find it?
+      if (!retrievedVertex.isPresent() || !retrievedVertex.get().getType().equals(type)) {
+        throw new CrudException("Failed to delete vertex with id: "
+            + id + " - vertex does not exist.",
+            javax.ws.rs.core.Response.Status.NOT_FOUND);
+      }
+
+      // Now, verify that there are no edges incident to the vertex (they must be deleted
+      // first if so).
+      Stream<ChampRelationship> relationships =
+          champApi.retrieveRelationships(retrievedVertex.get());
+
+      if (relationships.count() > 0) {
+        throw new CrudException("Attempt to delete vertex with id "
+            + id + " which has incident edges.",
+            javax.ws.rs.core.Response.Status.BAD_REQUEST);
+      }
+
+      // Finally, we can attempt to delete our vertex.
+      champApi.deleteObject(Long.parseLong(id));
+
+
+    } catch (NumberFormatException
+        | ChampUnmarshallingException
+        | ChampObjectNotExistsException e) {
+
+      throw new CrudException(e.getMessage(),
+          javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+    }
+  }
+
+  @Override
+  public void deleteEdge(String id, String type) throws CrudException {
+
+    try {
+
+      // First, retrieve the edge that we want to delete.
+      Optional<ChampRelationship> relationshipToDelete
+          = champApi.retrieveRelationship(getRelKey(id));
+
+
+      // Did we find it?
+      if (!relationshipToDelete.isPresent() || !relationshipToDelete.get().getType().equals(type)) {
+        throw new CrudException("Failed to delete edge with id: " + id + " - edge does not exist",
+            javax.ws.rs.core.Response.Status.NOT_FOUND);
+      }
+
+      // Now we can delete the edge.
+      champApi.deleteRelationship(relationshipToDelete.get());
+
+    } catch (ChampRelationshipNotExistsException
+        | NumberFormatException
+        | ChampUnmarshallingException e) {
+
+      throw new CrudException(e.getMessage(),
+          javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+    }
+  }
+
+
+  /**
+   * This helper method generates a string representation of a properties map for
+   * logging purposes.
+   *
+   * @param properties - The properties map to be converted.
+   * @return - The log statement friendly conversion of the properties map.
+   */
+  private String propertiesMapToString(Map<String, Object> properties) {
+
+    StringBuilder sb = new StringBuilder();
+    sb.append("{");
+
+    for (String key : properties.keySet()) {
+      sb.append("(").append(key).append(" -> ").append(properties.get(key)).append(") ");
+    }
+
+    sb.append("}");
+
+    return sb.toString();
+  }
+
+
+  /**
+   * This helper method constructs a {@link ChampObject} suitable for passing to the Champ library.
+   *
+   * @param type       - The type to assign to our ChampObject
+   * @param properties - The set of properties to assign to our ChampObject
+   * @return - A populated ChampObject
+   */
+  private ChampObject buildChampObject(String type, Map<String, Object> properties) {
+
+    ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create()
+        .ofType(type)
+        .withoutKey();
+
+    for (String key : properties.keySet()) {
+      objectInProgress.withProperty(key, properties.get(key));
+    }
+    return objectInProgress.build();
+  }
+
+
+  /**
+   * This helper method constructs a {@link ChampObject} suitable for passing to the Champ library.
+   *
+   * @param id         - Unique identifier for this object.
+   * @param type       - The type to assign to our ChampObject
+   * @param properties - The set of properties to assign to our ChampObject
+   * @return - A populated ChampObject
+   */
+  private ChampObject buildChampObject(String id, String type, Map<String, Object> properties) {
+
+    ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create()
+        .ofType(type)
+        .withKey(Long.parseLong(id));
+
+    for (String key : properties.keySet()) {
+      objectInProgress.withProperty(key, properties.get(key));
+    }
+    return objectInProgress.build();
+  }
+
+
+  
+  
+  private Vertex vertexFromChampObject(ChampObject champObject, String type) {
+
+    // Get the identifier for this vertex from the Champ object.
+    Object id = champObject.getKey().orElse("");
+
+    // Start building our {@link Vertex} object.
+    Vertex.Builder vertexBuilder = new Vertex.Builder(type);
+    vertexBuilder.id(id.toString());
+
+    // Convert the properties associated with the Champ object into the form expected for
+    // a Vertex object.
+    for (String key : champObject.getProperties().keySet()) {
+      vertexBuilder.property(key, champObject.getProperties().get(key));
+    }
+
+    // ...and return it.
+    return vertexBuilder.build();
+  }
+
+
+  /**
+   * This helper method converts a {@link ChampRelationship} from the Champ library into an
+   * equivalent {@link Edge} object that is understood by the CRUD Service.
+   *
+   * @param relationship - The ChampRelationship object to be converted.
+   * @return - An Edge object corresponding to the supplied ChampRelationship
+   */
+  private Edge edgeFromChampRelationship(ChampRelationship relationship) {
+
+    // Populate the edge's id, if available.
+    Object relationshipId = relationship.getKey().orElse("");
+
+    Edge.Builder edgeBuilder = new Edge.Builder(relationship.getType())
+        .id(relationshipId.toString());
+    edgeBuilder.source(vertexFromChampObject(relationship.getSource(),
+        relationship.getSource().getProperties()
+            .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null
+                ? relationship.getSource().getType()
+                : relationship.getSource().getProperties()
+                    .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()));
+    edgeBuilder.target(vertexFromChampObject(relationship.getTarget(),
+        relationship.getTarget().getProperties()
+            .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null
+                ? relationship.getTarget().getType()
+                : relationship.getTarget().getProperties()
+                    .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()));
+
+    for (String key : relationship.getProperties().keySet()) {
+      edgeBuilder.property(key, relationship.getProperties().get(key).toString());
+    }
+
+    return edgeBuilder.build();
+  }
+
+
+  /**
+   * Performs all one-time configuration operations which are required when creating
+   * a new instance of the DAO.
+   */
+  private void configure() {
+
+    // Instantiate the Champ library API.
+    try {
+
+      // Determine which back end we are using.
+      switch (getBackendTypeFromConfig()) {
+
+        case IN_MEMORY:
+
+          logger.info(CrudServiceMsgs.INSTANTIATE_GRAPH_DAO,
+              "In Memory",
+              daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME),
+              "Not applicable");
+
+          champApi = ChampGraph.Factory.newInstance(ChampGraph.Type.IN_MEMORY,
+              daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME));
+
+          break;
+
+        case TITAN:
+          try {
+            String db = daoConfig.getProperty(CONFIG_STORAGE_BACKEND_DB);
+            Short graphIdSuffix = (short) new Random().nextInt(Short.MAX_VALUE);
+            logger.info(CrudServiceMsgs.TITAN_GRAPH_INFO, GRAPH_UNQ_INSTANCE_ID_SUFFIX
+                + ": = " + graphIdSuffix);
+            if (db.equalsIgnoreCase(STORAGE_CASSANDRA_DB)) {
+              logger.info(CrudServiceMsgs.INSTANTIATE_GRAPH_DAO, "Titan with cassandra backend",
+                  daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME),
+                  daoConfig.getProperty(CONFIG_STORAGE_HOSTNAMES));
+
+              TitanChampGraphImpl.Builder champApiBuilder =
+                  new TitanChampGraphImpl.Builder(daoConfig.getProperty(CONFIG_GRAPH_NAME,
+                      DEFAULT_GRAPH_NAME))
+                      .property("storage.backend", "cassandrathrift")
+                      .property(GRAPH_UNQ_INSTANCE_ID_SUFFIX, graphIdSuffix)
+                      .property("storage.hostname", daoConfig.get(CONFIG_STORAGE_HOSTNAMES));
+
+              if (daoConfig.containsKey(CONFIG_EVENT_STREAM_PUBLISHER)) {
+                champApiBuilder.property("champ.event.stream.publisher",
+                    daoConfig.get(CONFIG_EVENT_STREAM_PUBLISHER));
+              }
+
+              if (daoConfig.containsKey(CONFIG_EVENT_STREAM_NUM_PUBLISHERS)) {
+                champApiBuilder.property("champ.event.stream.publisher-pool-size",
+                    daoConfig.get(CONFIG_EVENT_STREAM_NUM_PUBLISHERS));
+              }
+
+              champApi = champApiBuilder.build();
+
+            } else if (db.equalsIgnoreCase(STORAGE_HBASE_DB)) {
+
+              logger.info(CrudServiceMsgs.INSTANTIATE_GRAPH_DAO, "Titan with Hbase backend",
+                  daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME),
+                  daoConfig.getProperty(CONFIG_STORAGE_HOSTNAMES));
+              TitanChampGraphImpl.Builder champApiBuilder =
+                  new TitanChampGraphImpl.Builder(daoConfig
+                      .getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME))
+                      .property("storage.backend", "hbase")
+                      .property("storage.hbase.ext.zookeeper.znode.parent",
+                          daoConfig.get(CONFIG_HBASE_ZNODE_PARENT))
+                      .property("storage.port", daoConfig.get(CONFIG_STORAGE_PORT))
+                      .property(GRAPH_UNQ_INSTANCE_ID_SUFFIX, graphIdSuffix)
+                      .property("storage.hostname", daoConfig.get(CONFIG_STORAGE_HOSTNAMES));
+
+              if (daoConfig.containsKey(CONFIG_EVENT_STREAM_PUBLISHER)) {
+                champApiBuilder.property("champ.event.stream.publisher",
+                    daoConfig.get(CONFIG_EVENT_STREAM_PUBLISHER));
+              }
+
+              if (daoConfig.containsKey(CONFIG_EVENT_STREAM_NUM_PUBLISHERS)) {
+                champApiBuilder.property("champ.event.stream.publisher-pool-size",
+                    daoConfig.get(CONFIG_EVENT_STREAM_NUM_PUBLISHERS));
+              }
+              champApi = champApiBuilder.build();
+            } else {
+              logger.error(CrudServiceMsgs.INVALID_GRAPH_BACKEND,
+                  daoConfig.getProperty(CONFIG_STORAGE_BACKEND_DB));
+            }
+
+          } catch (com.thinkaurelius.titan.core.TitanException e) {
+
+            logger.error(CrudServiceMsgs.INSTANTIATE_GRAPH_BACKEND_ERR, "Titan", e.getMessage());
+          }
+
+
+          break;
+
+        default:
+          logger.error(CrudServiceMsgs.INVALID_GRAPH_BACKEND,
+              daoConfig.getProperty(CONFIG_STORAGE_BACKEND));
+          break;
+      }
+
+    } catch (CrudException e) {
+      logger.error(CrudServiceMsgs.INSTANTIATE_GRAPH_BACKEND_ERR,
+          daoConfig.getProperty(CONFIG_STORAGE_BACKEND), e.getMessage());
+    }
+  }
+
+
+  /**
+   * Performs any necessary shut down operations when the DAO is no longer needed.
+   */
+  public void close() {
+
+    if (champApi != null) {
+
+      logger.info(CrudServiceMsgs.STOPPING_CHAMP_DAO);
+
+      champApi.shutdown();
+    }
+  }
+
+
+  /**
+   * This helper function converts the 'graph back end type' config parameter into the
+   * corresponding {@link ChampAPI.Type}.
+   *
+   * @return - A {@link ChampAPI.Type}
+   * @throws CrudException
+   */
+  private ChampGraph.Type getBackendTypeFromConfig() throws CrudException {
+
+    // Get the back end type from the DAO's configuration properties.
+    String backend = daoConfig.getProperty(CONFIG_STORAGE_BACKEND, "in-memory");
+
+    // Now, find the appropriate ChampAPI type and return it.
+    if (backend.equals("in-memory")) {
+      return ChampGraph.Type.IN_MEMORY;
+    } else if (backend.equals("titan")) {
+      return ChampGraph.Type.TITAN;
+    }
+
+    // If we are here, then whatever was in the config properties didn't match to a supported
+    // back end type, so just throw an exception and let the caller figure it out.
+    throw new CrudException("Invalid graph backend type '" + backend + "' specified.",
+        javax.ws.rs.core.Response.Status.BAD_REQUEST);
+  }
+
+
+}
diff --git a/src/main/java/org/openecomp/crud/entity/Edge.java b/src/main/java/org/openecomp/crud/entity/Edge.java
new file mode 100644 (file)
index 0000000..803511f
--- /dev/null
@@ -0,0 +1,130 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.entity;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class Edge {
+  private static final Gson gson = new GsonBuilder().create();
+
+  private final Optional<String> id;
+  private final String type;
+  private final Map<String, Object> properties;
+  private final Vertex source;
+  private final Vertex target;
+
+
+  private Edge(Builder builder) {
+    this.id = builder.id;
+    this.type = builder.type;
+    this.source = builder.source;
+    this.target = builder.target;
+    this.properties = builder.properties;
+  }
+
+  public static class Builder {
+    private Optional<String> id = Optional.empty();
+    private final String type;
+    private Vertex source;
+    private Vertex target;
+    private final Map<String, Object> properties = new HashMap<String, Object>();
+
+    public Builder(String type) {
+      if (type == null) {
+        throw new IllegalArgumentException("Type cannot be null");
+      }
+
+      this.type = type;
+    }
+
+    public Builder id(String id) {
+      if (id == null) {
+        throw new IllegalArgumentException("id cannot be null");
+      }
+
+      this.id = Optional.of(id);
+      return this;
+    }
+
+    public Builder property(String key, Object value) {
+      if (key == null || value == null) {
+        throw new IllegalArgumentException("Property key/value cannot be null");
+      }
+      properties.put(key, value);
+      return this;
+    }
+
+    public Builder source(Vertex source) {
+      this.source = source;
+      return this;
+    }
+
+    public Builder target(Vertex target) {
+      this.target = target;
+      return this;
+    }
+
+    public Edge build() {
+      return new Edge(this);
+    }
+  }
+
+
+  @Override
+  public String toString() {
+    return "Edge [id=" + id + ", type=" + type + ", properties=" + properties
+        + ", source=" + source + ", target=" + target + "]";
+  }
+
+  public String toJson() {
+    return gson.toJson(this);
+  }
+
+  public Optional<String> getId() {
+    return id;
+  }
+
+  public String getType() {
+    return type;
+  }
+
+  public Map<String, Object> getProperties() {
+    return properties;
+  }
+
+  public Vertex getSource() {
+    return source;
+  }
+
+  public Vertex getTarget() {
+    return target;
+  }
+
+
+}
diff --git a/src/main/java/org/openecomp/crud/entity/Vertex.java b/src/main/java/org/openecomp/crud/entity/Vertex.java
new file mode 100644 (file)
index 0000000..1b0b97d
--- /dev/null
@@ -0,0 +1,102 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.entity;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class Vertex {
+  private static final Gson gson = new GsonBuilder().create();
+
+  private final Optional<String> id;
+  private final String type;
+  private final Map<String, Object> properties;
+
+
+  private Vertex(Builder builder) {
+    this.id = builder.id;
+    this.type = builder.type;
+    this.properties = builder.properties;
+  }
+
+  public static class Builder {
+    private Optional<String> id = Optional.empty();
+    private final String type;
+    private final Map<String, Object> properties = new HashMap<String, Object>();
+
+    public Builder(String type) {
+      if (type == null) {
+        throw new IllegalArgumentException("Type cannot be null");
+      }
+      this.type = type;
+    }
+
+    public Builder id(String id) {
+      if (id == null) {
+        throw new IllegalArgumentException("id cannot be null");
+      }
+
+      this.id = Optional.of(id);
+      return this;
+    }
+
+    public Builder property(String key, Object value) {
+      if (key == null || value == null) {
+        throw new IllegalArgumentException("Property key/value cannot be null");
+      }
+      properties.put(key, value);
+      return this;
+    }
+
+    public Vertex build() {
+      return new Vertex(this);
+    }
+  }
+
+  public String toJson() {
+    return gson.toJson(this);
+  }
+
+  @Override
+  public String toString() {
+    return "Vertex [id=" + id + ", type=" + type + ", properties=" + properties + "]";
+  }
+
+  public Optional<String> getId() {
+    return id;
+  }
+
+  public String getType() {
+    return type;
+  }
+
+  public Map<String, Object> getProperties() {
+    return properties;
+  }
+
+}
diff --git a/src/main/java/org/openecomp/crud/event/GraphEvent.java b/src/main/java/org/openecomp/crud/event/GraphEvent.java
new file mode 100644 (file)
index 0000000..392bf2d
--- /dev/null
@@ -0,0 +1,235 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.event;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.annotations.SerializedName;
+
+import org.openecomp.crud.exception.CrudException;
+
+import javax.ws.rs.core.Response.Status;
+
+public class GraphEvent {
+
+  public enum GraphEventOperation {
+    CREATE, UPDATE, DELETE
+  }
+
+  public enum GraphEventResult {
+    SUCCESS, FAILURE
+  }
+
+  private GraphEventOperation operation;
+
+  @SerializedName("transaction-id")
+  private String transactionId;
+
+  private long timestamp;
+
+  private GraphEventVertex vertex;
+
+  private GraphEventEdge edge;
+
+  private GraphEventResult result;
+
+  @SerializedName("error-message")
+  private String errorMessage;
+
+  private Status httpErrorStatus;
+
+  /**
+   * Marshaller/unmarshaller for converting to/from JSON.
+   */
+  private static final Gson gson = new GsonBuilder().disableHtmlEscaping()
+      .setPrettyPrinting().create();
+
+  public static Builder builder(GraphEventOperation operation) {
+    return new Builder(operation);
+  }
+
+  public GraphEventOperation getOperation() {
+    return operation;
+  }
+
+  public String getTransactionId() {
+    return transactionId;
+  }
+
+  public long getTimestamp() {
+    return timestamp;
+  }
+
+  public GraphEventVertex getVertex() {
+    return vertex;
+  }
+
+  public GraphEventEdge getEdge() {
+    return edge;
+  }
+
+  public GraphEventResult getResult() {
+    return result;
+  }
+
+  public String getErrorMessage() {
+    return errorMessage;
+  }
+
+  public void setResult(GraphEventResult result) {
+    this.result = result;
+  }
+
+
+  public Status getHttpErrorStatus() {
+    return httpErrorStatus;
+  }
+
+  public void setHttpErrorStatus(Status httpErrorStatus) {
+    this.httpErrorStatus = httpErrorStatus;
+  }
+
+  public void setTimestamp(long timestamp) {
+    this.timestamp = timestamp;
+  }
+
+  public void setErrorMessage(String errorMessage) {
+    this.errorMessage = errorMessage;
+  }
+
+  public void setVertex(GraphEventVertex vertex) {
+    this.vertex = vertex;
+  }
+
+  public void setEdge(GraphEventEdge edge) {
+    this.edge = edge;
+  }
+
+  /**
+   * Unmarshalls this Vertex object into a JSON string.
+   *
+   * @return - A JSON format string representation of this Vertex.
+   */
+  public String toJson() {
+    return gson.toJson(this);
+  }
+
+  /**
+   * Marshalls the provided JSON string into a Vertex object.
+   *
+   * @param json - The JSON string to produce the Vertex from.
+   * @return - A Vertex object.
+   * @throws SpikeException
+   */
+  public static GraphEvent fromJson(String json) throws CrudException {
+
+    try {
+
+      // Make sure that we were actually provided a non-empty string
+      // before we
+      // go any further.
+      if (json == null || json.isEmpty()) {
+        throw new CrudException("Empty or null JSON string.", Status.BAD_REQUEST);
+      }
+
+      // Marshall the string into a Vertex object.
+      return gson.fromJson(json, GraphEvent.class);
+
+    } catch (Exception ex) {
+      throw new CrudException("Unable to parse JSON string: ", Status.BAD_REQUEST);
+    }
+  }
+
+  @Override
+  public String toString() {
+
+    return toJson();
+  }
+
+  public String getObjectKey() {
+    if (this.getVertex() != null) {
+      return this.getVertex().getId();
+    } else if (this.getEdge() != null) {
+      return this.getEdge().getId();
+    }
+
+    return null;
+
+  }
+
+  public String getObjectType() {
+    if (this.getVertex() != null) {
+      return "vertex->" + this.getVertex().getType();
+    } else if (this.getEdge() != null) {
+      return "edge->" + this.getEdge().getType();
+    }
+
+    return null;
+
+  }
+
+  public static class Builder {
+
+    GraphEvent event = null;
+
+    public Builder(GraphEventOperation operation) {
+      event = new GraphEvent();
+      event.operation = operation;
+    }
+
+    public Builder vertex(GraphEventVertex vertex) {
+      event.vertex = vertex;
+      return this;
+    }
+
+    public Builder edge(GraphEventEdge edge) {
+      event.edge = edge;
+      return this;
+    }
+
+    public Builder result(GraphEventResult result) {
+      event.result = result;
+      return this;
+    }
+
+    public Builder errorMessage(String errorMessage) {
+      event.errorMessage = errorMessage;
+      return this;
+    }
+
+    public Builder httpErrorStatus(Status httpErrorStatus) {
+      event.httpErrorStatus = httpErrorStatus;
+      return this;
+    }
+
+    public GraphEvent build() {
+
+      event.timestamp = System.currentTimeMillis();
+      event.transactionId = java.util.UUID.randomUUID().toString();
+
+      return event;
+    }
+  }
+
+}
diff --git a/src/main/java/org/openecomp/crud/event/GraphEventEdge.java b/src/main/java/org/openecomp/crud/event/GraphEventEdge.java
new file mode 100644 (file)
index 0000000..aaf9b72
--- /dev/null
@@ -0,0 +1,212 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.event;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.annotations.SerializedName;
+import com.google.gson.reflect.TypeToken;
+
+import org.openecomp.crud.entity.Edge;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+
+import java.util.Map;
+import javax.ws.rs.core.Response.Status;
+
+/**
+ * This class provides a generic representation of an Edge as provided by the
+ * graph data store.
+ */
+public class GraphEventEdge {
+
+  /**
+   * The unique identifier used to identify this edge in the graph data store.
+   */
+  @SerializedName("key")
+  private String id;
+
+  @SerializedName("schema-version")
+  private String modelVersion;
+
+  /**
+   * Type label assigned to this vertex.
+   */
+  private String type;
+
+  /**
+   * Source vertex for our edge.
+   */
+  private GraphEventVertex source;
+
+  /**
+   * Target vertex for our edge.
+   */
+  private GraphEventVertex target;
+
+  /**
+   * Map of all of the properties assigned to this vertex.
+   */
+  private JsonElement properties;
+
+  /**
+   * Marshaller/unmarshaller for converting to/from JSON.
+   */
+  private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
+
+  public GraphEventEdge(String id, String modelVersion, String type, GraphEventVertex source,
+                        GraphEventVertex target, JsonElement properties) {
+    this.id = id;
+    this.modelVersion = modelVersion;
+    this.type = type;
+    this.source = source;
+    this.target = target;
+    this.properties = properties;
+  }
+
+  public GraphEventEdge() {
+
+  }
+
+  public String getId() {
+    return id;
+  }
+
+  public void setId(String id) {
+    this.id = id;
+  }
+
+  public String getType() {
+    return type;
+  }
+
+  public void setType(String type) {
+    this.type = type;
+  }
+
+  public GraphEventVertex getSource() {
+    return source;
+  }
+
+  public void setSource(GraphEventVertex source) {
+    this.source = source;
+  }
+
+  public GraphEventVertex getTarget() {
+    return target;
+  }
+
+  public void setTarget(GraphEventVertex target) {
+    this.target = target;
+  }
+
+  public JsonElement getProperties() {
+    return properties;
+  }
+
+  public void setProperties(JsonElement properties) {
+    this.properties = properties;
+  }
+
+  public String getModelVersion() {
+    return modelVersion;
+  }
+
+  public void setModelVersion(String modelVersion) {
+    this.modelVersion = modelVersion;
+  }
+
+  /**
+   * Unmarshalls this Edge object into a JSON string.
+   *
+   * @return - A JSON format string representation of this Edge.
+   */
+  public String toJson() {
+    return gson.toJson(this);
+  }
+
+  /**
+   * Marshalls the provided JSON string into a Edge object.
+   *
+   * @param json - The JSON string to produce the Edge from.
+   * @return - A Edge object.
+   * @throws SpikeException
+   */
+  public static GraphEventEdge fromJson(String json) throws CrudException {
+
+    try {
+
+      // Make sure that we were actually provided a non-empty string
+      // before we
+      // go any further.
+      if (json == null || json.isEmpty()) {
+        throw new CrudException("Unable to parse JSON string: ", Status.BAD_REQUEST);
+      }
+
+      // Marshall the string into an Edge object.
+      return gson.fromJson(json, GraphEventEdge.class);
+
+    } catch (Exception ex) {
+      throw new CrudException("Unable to parse JSON string: ", Status.BAD_REQUEST);
+    }
+  }
+
+  public static GraphEventEdge fromEdge(Edge edge, String modelVersion) {
+
+    java.lang.reflect.Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
+    JsonObject props = gson.toJsonTree(edge.getProperties(), mapType).getAsJsonObject();
+
+    GraphEventEdge graphEventEdge = new GraphEventEdge(edge.getId().orElse(""), modelVersion,
+        edge.getType(), new GraphEventVertex(edge.getSource().getId().orElse(""), null,
+        edge.getSource().getType(), null), new GraphEventVertex(edge.getTarget().getId().orElse(""),
+        null, edge.getTarget().getType(), null), props);
+
+    return graphEventEdge;
+
+  }
+
+  public Edge toEdge() {
+    Edge.Builder builder = new Edge.Builder(this.getType()).id(this.getId());
+    if (this.getSource() != null) {
+      builder.source(new Vertex.Builder(this.getSource().getType()).id(this.getSource().getId())
+          .build());
+    }
+    if (this.getTarget() != null) {
+      builder.target(new Vertex.Builder(this.getTarget().getType()).id(this.getTarget().getId())
+          .build());
+    }
+
+    if (this.getProperties() != null) {
+      java.lang.reflect.Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
+      Map<String, Object> propertiesMap = gson.fromJson(this.getProperties(), mapType);
+      for (String key : propertiesMap.keySet()) {
+        builder.property(key, propertiesMap.get(key));
+      }
+    }
+    return builder.build();
+
+  }
+}
diff --git a/src/main/java/org/openecomp/crud/event/GraphEventVertex.java b/src/main/java/org/openecomp/crud/event/GraphEventVertex.java
new file mode 100644 (file)
index 0000000..7fde12a
--- /dev/null
@@ -0,0 +1,181 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.event;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.annotations.SerializedName;
+import com.google.gson.reflect.TypeToken;
+
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+
+import java.util.Map;
+import javax.ws.rs.core.Response.Status;
+
+/**
+ * This class provides a generic representation of a Vertex as provided by the
+ * graph data store.
+ */
+public class GraphEventVertex {
+
+  /**
+   * The unique identifier used to identify this vertex in the graph data
+   * store.
+   */
+  @SerializedName("key")
+  private String id;
+
+  @SerializedName("schema-version")
+  private String modelVersion;
+
+  /**
+   * Type label assigned to this vertex.
+   */
+  private String type;
+
+  /**
+   * Map of all of the properties assigned to this vertex.
+   */
+  private JsonElement properties;
+
+  /**
+   * Marshaller/unmarshaller for converting to/from JSON.
+   */
+  private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
+
+  public GraphEventVertex(String id, String modelVersion, String type, JsonElement properties) {
+    this.id = id;
+    this.modelVersion = modelVersion;
+    this.type = type;
+    this.properties = properties;
+  }
+
+  public GraphEventVertex() {
+
+  }
+
+  public String getId() {
+    return id;
+  }
+
+  public void setId(String id) {
+    this.id = id;
+  }
+
+  public String getType() {
+    return type;
+  }
+
+  public void setType(String type) {
+    this.type = type;
+  }
+
+
+  public JsonElement getProperties() {
+    return properties;
+  }
+
+  public void setProperties(JsonElement properties) {
+    this.properties = properties;
+  }
+
+  public String getModelVersion() {
+    return modelVersion;
+  }
+
+  public void setModelVersion(String modelVersion) {
+    this.modelVersion = modelVersion;
+  }
+
+  /**
+   * Unmarshalls this Vertex object into a JSON string.
+   *
+   * @return - A JSON format string representation of this Vertex.
+   */
+  public String toJson() {
+    return gson.toJson(this);
+  }
+
+  /**
+   * Marshalls the provided JSON string into a Vertex object.
+   *
+   * @param json - The JSON string to produce the Vertex from.
+   * @return - A Vertex object.
+   * @throws SpikeException
+   */
+  public static GraphEventVertex fromJson(String json) throws CrudException {
+
+    try {
+
+      // Make sure that we were actually provided a non-empty string
+      // before we
+      // go any further.
+      if (json == null || json.isEmpty()) {
+        throw new CrudException("Empty or null JSON string.", Status.BAD_REQUEST);
+      }
+
+      // Marshall the string into a Vertex object.
+      return gson.fromJson(json, GraphEventVertex.class);
+
+    } catch (Exception ex) {
+      throw new CrudException("Unable to parse JSON string: ", Status.BAD_REQUEST);
+    }
+  }
+
+  @Override
+  public String toString() {
+
+    return toJson();
+  }
+
+  public static GraphEventVertex fromVertex(Vertex vertex, String modelVersion) {
+
+    java.lang.reflect.Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
+    JsonObject props = gson.toJsonTree(vertex.getProperties(), mapType).getAsJsonObject();
+    GraphEventVertex graphEventVertex = new GraphEventVertex(vertex.getId().orElse(""),
+        modelVersion, vertex.getType(), props);
+    return graphEventVertex;
+
+  }
+
+  public Vertex toVertex() {
+    Vertex.Builder builder = new Vertex.Builder(this.getType()).id(this.getId());
+
+    if (this.getProperties() != null) {
+      java.lang.reflect.Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
+      Map<String, Object> propertiesMap = gson.fromJson(this.getProperties(), mapType);
+      for (String key : propertiesMap.keySet()) {
+        builder.property(key, propertiesMap.get(key));
+      }
+    }
+
+    return builder.build();
+
+  }
+
+
+}
diff --git a/src/main/java/org/openecomp/crud/exception/CrudException.java b/src/main/java/org/openecomp/crud/exception/CrudException.java
new file mode 100644 (file)
index 0000000..2911070
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.exception;
+
+import javax.ws.rs.core.Response.Status;
+
+public class CrudException extends Exception {
+
+  private static final long serialVersionUID = 8162385108397238865L;
+
+  private Status httpStatus;
+
+  public CrudException() {
+  }
+
+  public CrudException(String message, Status httpStatus) {
+    super(message);
+    this.setHttpStatus(httpStatus);
+  }
+
+  public CrudException(Throwable cause) {
+    super(cause);
+  }
+
+  public CrudException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+  public CrudException(String message, Throwable cause, boolean enableSuppression,
+                       boolean writableStackTrace) {
+    super(message, cause, enableSuppression, writableStackTrace);
+  }
+
+  public Status getHttpStatus() {
+    return httpStatus;
+  }
+
+  public void setHttpStatus(Status httpStatus) {
+    this.httpStatus = httpStatus;
+  }
+}
diff --git a/src/main/java/org/openecomp/crud/logging/CrudServiceMsgs.java b/src/main/java/org/openecomp/crud/logging/CrudServiceMsgs.java
new file mode 100644 (file)
index 0000000..71fb14b
--- /dev/null
@@ -0,0 +1,128 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.logging;
+
+import com.att.eelf.i18n.EELFResourceManager;
+
+import org.openecomp.cl.eelf.LogMessageEnum;
+
+public enum CrudServiceMsgs implements LogMessageEnum {
+
+  /**
+   * Received request {0} {1} from {2}.  Sending response: {3}
+   *
+   * <p>Arguments:
+   * {0} = operation
+   * {1} = target URL
+   * {2} = source
+   * {3} = response code
+   */
+  PROCESS_REST_REQUEST,
+
+  INVALID_OXM_FILE,
+  INVALID_OXM_DIR,
+  OXM_FILE_CHANGED,
+
+  /**
+   * Successfully loaded schema: {0}
+   *
+   * <p>Arguments:
+   * {0} = oxm filename
+   */
+  LOADED_OXM_FILE,
+
+  /**
+   * Unable to load OXM schema: {0}
+   *
+   * <p>Arguments:
+   * {0} = error
+   */
+  OXM_LOAD_ERROR,
+
+  /**
+   * Instantiate data access layer for graph data store type: {0} graph: {1} using hosts: {2}
+   *
+   * <p>Arguments:
+   * {0} = Graph data store technology type
+   * {1} = Graph name
+   * {2} = Hosts list
+   */
+  INSTANTIATE_GRAPH_DAO,
+
+  /**
+   * Stopping ChampDAO...
+   *
+   * <p>Arguments:
+   */
+  STOPPING_CHAMP_DAO,
+
+  /**
+   * Unsupported graph database {0} specified.
+   *
+   * <p>Arguments:
+   * {0} = Graph database back end.
+   */
+  INVALID_GRAPH_BACKEND,
+
+  /**
+   * Failure instantiating {0} graph database backend.  Cause: {1}
+   *
+   * <p>Arguments:
+   * {0} - Graph database type.
+   * {1} - Failure cause.
+   */
+  INSTANTIATE_GRAPH_BACKEND_ERR,
+
+  /**
+   * Failure instantiating CRUD Rest Service.  Cause: {0}
+   *
+   * <p>Arguments:
+   * {0} - Failure cause.
+   */
+  INSTANTIATE_AUTH_ERR,
+
+  /**
+   * Any info log related to titan graph
+   *
+   * <p>Arguments:
+   * {0} - Info.
+   */
+  TITAN_GRAPH_INFO,
+  
+  /**
+   * Arguments:
+   * {0} Opertaion
+   * {1} URI
+   * {2} = Exception
+   */
+  EXCEPTION_DURING_METHOD_CALL;
+
+
+  /**
+   * Static initializer to ensure the resource bundles for this class are loaded...
+   */
+  static {
+    EELFResourceManager.loadMessageBundle("logging/CrudServiceMsgs");
+  }
+}
diff --git a/src/main/java/org/openecomp/crud/logging/LoggingUtil.java b/src/main/java/org/openecomp/crud/logging/LoggingUtil.java
new file mode 100644 (file)
index 0000000..aaa250a
--- /dev/null
@@ -0,0 +1,88 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.logging;
+
+import org.openecomp.cl.api.LogFields;
+import org.openecomp.cl.api.LogLine;
+import org.openecomp.cl.api.Logger;
+import org.openecomp.cl.mdc.MdcContext;
+import org.openecomp.crud.util.CrudServiceConstants;
+import org.slf4j.MDC;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+
+public class LoggingUtil {
+  /**
+   * Initializes mdc context.
+   */
+  public static void initMdcContext(HttpServletRequest httpReq, HttpHeaders headers) {
+    String fromIp = httpReq.getRemoteAddr();
+    String fromAppId = "";
+    String transId = null;
+
+    if (headers.getRequestHeaders().getFirst("X-FromAppId") != null) {
+      fromAppId = headers.getRequestHeaders().getFirst("X-FromAppId");
+    }
+
+    if ((headers.getRequestHeaders().getFirst("X-TransactionId") == null)
+        || headers.getRequestHeaders().getFirst("X-TransactionId").isEmpty()) {
+      transId = java.util.UUID.randomUUID().toString();
+    } else {
+      transId = headers.getRequestHeaders().getFirst("X-TransactionId");
+    }
+
+    MdcContext.initialize(transId, CrudServiceConstants.CRD_SERVICE_NAME, "", fromAppId, fromIp);
+  }
+
+  /**
+   * Logs the rest request.
+   */
+  public static void logRestRequest(Logger logger, Logger auditLogger,
+                                    HttpServletRequest req, Response response) {
+    String respStatusString = "";
+    if (Response.Status.fromStatusCode(response.getStatus()) != null) {
+      respStatusString = Response.Status.fromStatusCode(response.getStatus()).toString();
+    }
+
+    // Generate error log
+    logger.info(CrudServiceMsgs.PROCESS_REST_REQUEST, req.getMethod(),
+        req.getRequestURL().toString(), req.getRemoteHost(),
+        Integer.toString(response.getStatus()));
+
+    // Generate audit log.
+    auditLogger.info(CrudServiceMsgs.PROCESS_REST_REQUEST,
+        new LogFields()
+            .setField(LogLine.DefinedFields.RESPONSE_CODE, response.getStatus())
+            .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, respStatusString),
+        (req != null) ? req.getMethod() : "Unknown",
+        (req != null) ? req.getRequestURL().toString() : "Unknown",
+        (req != null) ? req.getRemoteHost() : "Unknown",
+        Integer.toString(response.getStatus()) + " error: " + (response.getEntity() == null ? ""
+            : response.getEntity().toString()));
+    MDC.clear();
+  }
+}
diff --git a/src/main/java/org/openecomp/crud/parser/CrudResponseBuilder.java b/src/main/java/org/openecomp/crud/parser/CrudResponseBuilder.java
new file mode 100644 (file)
index 0000000..1a84322
--- /dev/null
@@ -0,0 +1,185 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.parser;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+
+import org.openecomp.crud.entity.Edge;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.service.EdgePayload;
+import org.openecomp.crud.service.VertexPayload;
+import org.openecomp.schema.RelationshipSchemaLoader;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CrudResponseBuilder {
+
+  private static final Gson gson = new GsonBuilder().create();
+
+  public static final String SOURCE = "source";
+  public static final String TARGET = "target";
+  public static final String URL_BASE = "services/inventory/";
+
+  public static String buildUpsertVertexResponse(Vertex vertex, String version)
+      throws CrudException {
+    VertexPayload payload = new VertexPayload();
+    payload.setId(vertex.getId().get());
+    payload.setType(vertex.getType());
+    payload.setUrl(URL_BASE + version + "/" + vertex.getType() + "/" + vertex.getId().get());
+    JsonObject props = new JsonObject();
+    for (String key : vertex.getProperties().keySet()) {
+      addJsonProperperty(props, key, vertex.getProperties().get(key));
+    }
+    payload.setProperties(props);
+    return payload.toJson();
+  }
+
+  public static String buildUpsertEdgeResponse(Edge edge, String version) throws CrudException {
+    return buildGetEdgeResponse(edge, version);
+  }
+
+  public static String buildGetVertexResponse(Vertex vertex, List<Edge> edges, String version)
+      throws CrudException {
+    VertexPayload vertexPayload = new VertexPayload();
+    vertexPayload.setId(vertex.getId().get());
+    vertexPayload.setType(vertex.getType());
+    vertexPayload.setUrl(URL_BASE + version + "/" + vertex.getType() + "/" + vertex.getId().get());
+    JsonObject props = new JsonObject();
+    for (String key : vertex.getProperties().keySet()) {
+      addJsonProperperty(props, key, vertex.getProperties().get(key));
+    }
+    vertexPayload.setProperties(props);
+    List<EdgePayload> inEdges = new ArrayList<EdgePayload>();
+    List<EdgePayload> outEdges = new ArrayList<EdgePayload>();
+    for (Edge e : edges) {
+      if (e.getTarget().getId().get().equals(vertex.getId().get())) {
+        EdgePayload inEdge = new EdgePayload();
+        inEdge.setId(e.getId().get());
+        inEdge.setType(e.getType());
+        inEdge.setUrl(URL_BASE + "relationships/"
+            + RelationshipSchemaLoader.getLatestSchemaVersion()
+            + "/" + e.getType() + "/" + e.getId().get());
+        inEdge.setSource(
+            URL_BASE + version + "/" + e.getSource().getType() + "/" + e.getSource().getId().get());
+
+        inEdges.add(inEdge);
+      } else if (e.getSource().getId().get().equals(vertex.getId().get())) {
+        EdgePayload outEdge = new EdgePayload();
+        outEdge.setId(e.getId().get());
+        outEdge.setType(e.getType());
+        outEdge.setUrl(URL_BASE + "relationships/"
+            + RelationshipSchemaLoader.getLatestSchemaVersion()
+            + "/" + e.getType() + "/" + e.getId().get());
+        outEdge.setTarget(
+            URL_BASE + version + "/" + e.getTarget().getType() + "/" + e.getTarget().getId().get());
+        outEdges.add(outEdge);
+      }
+    }
+
+
+    vertexPayload.setIn(inEdges);
+    vertexPayload.setOut(outEdges);
+
+    return vertexPayload.toJson();
+  }
+
+  public static String buildGetVerticesResponse(List<Vertex> items, String version)
+      throws CrudException {
+
+    JsonArray arry = new JsonArray();
+    for (Vertex v : items) {
+      JsonObject item = new JsonObject();
+      item.addProperty("id", v.getId().get());
+      item.addProperty("type", v.getType());
+      item.addProperty("url", "services/inventory/" + version + "/"
+          + v.getType() + "/" + v.getId().get());
+
+      arry.add(item);
+    }
+
+    return gson.toJson(arry);
+  }
+
+  public static String buildGetEdgeResponse(Edge edge, String version) throws CrudException {
+
+    EdgePayload payload = new EdgePayload();
+    payload.setId(edge.getId().get());
+    payload.setType(edge.getType());
+    payload.setUrl(URL_BASE + "relationships/" + version + "/" + edge.getType()
+        + "/" + edge.getId().get());
+    payload.setSource(
+        URL_BASE + version + "/" + edge.getSource().getType()
+            + "/" + edge.getSource().getId().get());
+    payload.setTarget(
+        URL_BASE + version + "/" + edge.getTarget().getType()
+            + "/" + edge.getTarget().getId().get());
+
+    JsonObject props = new JsonObject();
+    for (String key : edge.getProperties().keySet()) {
+      addJsonProperperty(props, key, edge.getProperties().get(key));
+    }
+    payload.setProperties(props);
+    return payload.toJson();
+  }
+
+  public static String buildGetEdgesResponse(List<Edge> items, String version)
+      throws CrudException {
+
+    JsonArray arry = new JsonArray();
+    for (Edge e : items) {
+      JsonObject item = new JsonObject();
+      item.addProperty("id", e.getId().get());
+      item.addProperty("type", e.getType());
+      item.addProperty("url", URL_BASE + "relationships/" + version + "/" + e.getType()
+          + "/" + e.getId().get());
+      item.addProperty(SOURCE, "services/inventory/" + version + "/" + e.getSource().getType()
+          + "/" + e.getSource().getId().get());
+      item.addProperty(TARGET, "services/inventory/" + version + "/" + e.getTarget().getType()
+          + "/" + e.getTarget().getId().get());
+      arry.add(item);
+    }
+
+    return gson.toJson(arry);
+  }
+
+  private static void addJsonProperperty(JsonObject jsonObj, String key, Object value) {
+    if (value instanceof Integer) {
+      jsonObj.addProperty(key, (Integer) value);
+    } else if (value instanceof Boolean) {
+      jsonObj.addProperty(key, (Boolean) value);
+    } else if (value instanceof Double) {
+      jsonObj.addProperty(key, (Double) value);
+    } else if (value instanceof String) {
+      jsonObj.addProperty(key, (String) value);
+    } else {
+      jsonObj.addProperty(key, value.toString());
+    }
+  }
+
+}
diff --git a/src/main/java/org/openecomp/crud/service/CrudGraphDataService.java b/src/main/java/org/openecomp/crud/service/CrudGraphDataService.java
new file mode 100644 (file)
index 0000000..ce60c3c
--- /dev/null
@@ -0,0 +1,209 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.service;
+
+import com.att.ecomp.event.api.EventPublisher;
+
+import org.openecomp.crud.dao.GraphDao;
+import org.openecomp.crud.dao.champ.ChampDao;
+import org.openecomp.crud.entity.Edge;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.parser.CrudResponseBuilder;
+import org.openecomp.crud.util.CrudProperties;
+import org.openecomp.crud.util.CrudServiceConstants;
+import org.openecomp.schema.OxmModelLoader;
+import org.openecomp.schema.OxmModelValidator;
+import org.openecomp.schema.RelationshipSchemaLoader;
+import org.openecomp.schema.RelationshipSchemaValidator;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+public class CrudGraphDataService {
+
+  private GraphDao dao;
+
+  public CrudGraphDataService(EventPublisher champEventPublisher) throws CrudException {
+
+    // Configure the GraphDao and wire it
+    Properties champProperties = new Properties();
+    champProperties.put(ChampDao.CONFIG_STORAGE_BACKEND, "titan");
+    champProperties.put(ChampDao.CONFIG_STORAGE_BACKEND_DB,
+        CrudProperties.get(CrudServiceConstants.CRD_STORAGE_BACKEND_DB, "hbase"));
+    champProperties.put(ChampDao.CONFIG_STORAGE_HOSTNAMES,
+        CrudProperties.get(CrudServiceConstants.CRD_GRAPH_HOST));
+    champProperties.put(ChampDao.CONFIG_STORAGE_PORT,
+        CrudProperties.get(CrudServiceConstants.CRD_GRAPH_PORT, "2181"));
+    champProperties.put(ChampDao.CONFIG_HBASE_ZNODE_PARENT,
+        CrudProperties.get(CrudServiceConstants.CRD_HBASE_ZNODE_PARENT, "/hbase-unsecure"));
+
+    if (CrudProperties.get("crud.graph.name") != null) {
+      champProperties.put(ChampDao.CONFIG_GRAPH_NAME, CrudProperties.get("crud.graph.name"));
+    }
+
+    if (champEventPublisher != null) {
+      champProperties.put(ChampDao.CONFIG_EVENT_STREAM_PUBLISHER, champEventPublisher);
+    }
+
+    if (CrudProperties.get(ChampDao.CONFIG_EVENT_STREAM_NUM_PUBLISHERS) != null) {
+      champProperties.put(ChampDao.CONFIG_EVENT_STREAM_NUM_PUBLISHERS,
+          Integer.parseInt(CrudProperties.get(ChampDao.CONFIG_EVENT_STREAM_NUM_PUBLISHERS)));
+    }
+
+    ChampDao champDao = new ChampDao(champProperties);
+
+    this.dao = champDao;
+
+    //load the schemas
+    OxmModelLoader.loadModels();
+    RelationshipSchemaLoader.loadModels();
+  }
+
+
+  public String addVertex(String version, String type, VertexPayload payload) throws CrudException {
+    Vertex vertex = OxmModelValidator.validateIncomingUpsertPayload(null, version, type,
+        payload.getProperties());
+    return addVertex(version, vertex);
+  }
+
+  private String addVertex(String version, Vertex vertex) throws CrudException {
+    Vertex addedVertex = dao.addVertex(vertex.getType(), vertex.getProperties());
+    return CrudResponseBuilder
+        .buildUpsertVertexResponse(OxmModelValidator.validateOutgoingPayload(version, addedVertex),
+            version);
+  }
+
+  public String addEdge(String version, String type, EdgePayload payload) throws CrudException {
+    Edge edge = RelationshipSchemaValidator.validateIncomingAddPayload(version, type, payload);
+    return addEdge(version, edge);
+  }
+
+  private String addEdge(String version, Edge edge) throws CrudException {
+    Edge addedEdge = dao.addEdge(edge.getType(), edge.getSource(), edge.getTarget(),
+        edge.getProperties());
+    return CrudResponseBuilder.buildUpsertEdgeResponse(
+        RelationshipSchemaValidator.validateOutgoingPayload(version, addedEdge), version);
+  }
+
+  public String getEdge(String version, String id, String type) throws CrudException {
+    RelationshipSchemaValidator.validateType(version, type);
+    Edge edge = dao.getEdge(id, type);
+
+    return CrudResponseBuilder.buildGetEdgeResponse(RelationshipSchemaValidator
+            .validateOutgoingPayload(version, edge),
+        version);
+  }
+
+  public String getEdges(String version, String type, Map<String, String> filter)
+      throws CrudException {
+    RelationshipSchemaValidator.validateType(version, type);
+    List<Edge> items = dao.getEdges(type, RelationshipSchemaValidator
+        .resolveCollectionfilter(version, type, filter));
+    return CrudResponseBuilder.buildGetEdgesResponse(items, version);
+  }
+
+
+  public String updateVertex(String version, String id, String type, VertexPayload payload)
+      throws CrudException {
+    Vertex vertex = OxmModelValidator.validateIncomingUpsertPayload(id, version, type,
+        payload.getProperties());
+    return updateVertex(version, vertex);
+
+  }
+
+  private String updateVertex(String version, Vertex vertex) throws CrudException {
+    Vertex updatedVertex = dao.updateVertex(vertex.getId().get(), vertex.getType(),
+        vertex.getProperties());
+    return CrudResponseBuilder
+        .buildUpsertVertexResponse(OxmModelValidator.validateOutgoingPayload(version,
+            updatedVertex), version);
+  }
+
+  public String patchVertex(String version, String id, String type, VertexPayload payload)
+      throws CrudException {
+    Vertex existingVertex = dao.getVertex(id, OxmModelValidator.resolveCollectionType(version,
+        type));
+    Vertex vertex = OxmModelValidator.validateIncomingPatchPayload(id, version, type,
+        payload.getProperties(), existingVertex);
+    return updateVertex(version, vertex);
+
+  }
+
+  public String deleteVertex(String version, String id, String type) throws CrudException {
+    type = OxmModelValidator.resolveCollectionType(version, type);
+    dao.deleteVertex(id, type);
+    return "";
+
+  }
+
+  public String deleteEdge(String version, String id, String type) throws CrudException {
+    RelationshipSchemaValidator.validateType(version, type);
+    dao.deleteEdge(id, type);
+    return "";
+
+  }
+
+  public String updateEdge(String version, String id, String type, EdgePayload payload)
+      throws CrudException {
+    Edge edge = dao.getEdge(id, type);
+    Edge validatedEdge = RelationshipSchemaValidator.validateIncomingUpdatePayload(edge,
+        version, payload);
+    return updateEdge(version, validatedEdge);
+
+  }
+
+  private String updateEdge(String version, Edge edge) throws CrudException {
+    Edge updatedEdge = dao.updateEdge(edge);
+    return CrudResponseBuilder.buildUpsertEdgeResponse(
+        RelationshipSchemaValidator.validateOutgoingPayload(version, updatedEdge), version);
+  }
+
+  public String patchEdge(String version, String id, String type, EdgePayload payload)
+      throws CrudException {
+    Edge edge = dao.getEdge(id, type);
+    Edge patchedEdge = RelationshipSchemaValidator.validateIncomingPatchPayload(edge,
+        version, payload);
+    return updateEdge(version, patchedEdge);
+
+  }
+
+  public String getVertex(String version, String id, String type) throws CrudException {
+    type = OxmModelValidator.resolveCollectionType(version, type);
+    Vertex vertex = dao.getVertex(id, type);
+    List<Edge> edges = dao.getVertexEdges(id);
+    return CrudResponseBuilder.buildGetVertexResponse(OxmModelValidator
+            .validateOutgoingPayload(version, vertex), edges, version);
+  }
+
+  public String getVertices(String version, String type, Map<String, String> filter)
+      throws CrudException {
+    type = OxmModelValidator.resolveCollectionType(version, type);
+    List<Vertex> items = dao.getVertices(type, OxmModelValidator.resolveCollectionfilter(version,
+        type, filter));
+    return CrudResponseBuilder.buildGetVerticesResponse(items, version);
+  }
+
+}
diff --git a/src/main/java/org/openecomp/crud/service/CrudRestService.java b/src/main/java/org/openecomp/crud/service/CrudRestService.java
new file mode 100644 (file)
index 0000000..09193ad
--- /dev/null
@@ -0,0 +1,686 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.service;
+
+import org.apache.cxf.jaxrs.ext.PATCH;
+import org.openecomp.auth.Auth;
+import org.openecomp.cl.api.Logger;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.logging.CrudServiceMsgs;
+import org.openecomp.crud.logging.LoggingUtil;
+import org.openecomp.crud.util.CrudServiceConstants;
+import org.slf4j.MDC;
+
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.security.auth.x500.X500Principal;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.Encoded;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriInfo;
+
+public class CrudRestService {
+
+  private CrudGraphDataService crudGraphDataService;
+  Logger logger = LoggerFactory.getInstance().getLogger(CrudRestService.class.getName());
+  Logger auditLogger = LoggerFactory.getInstance().getAuditLogger(CrudRestService.class.getName());
+  private Auth auth;
+
+  private String mediaType = MediaType.APPLICATION_JSON;
+  public static final String HTTP_PATCH_METHOD_OVERRIDE = "X-HTTP-Method-Override";
+
+  public CrudRestService(CrudGraphDataService crudGraphDataService) throws Exception {
+    this.crudGraphDataService = crudGraphDataService;
+    this.auth = new Auth(CrudServiceConstants.CRD_AUTH_FILE);
+  }
+
+  public enum Action {
+    POST, GET, PUT, DELETE, PATCH
+  }
+
+  ;
+
+  public void startup() {
+
+  }
+
+  @GET
+  @Path("/{version}/{type}/{id}")
+  @Consumes({MediaType.APPLICATION_JSON})
+  @Produces({MediaType.APPLICATION_JSON})
+  public Response getVertex(String content, @PathParam("version") String version,
+                            @PathParam("type") String type, @PathParam("id") String id,
+                            @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+                            @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+    LoggingUtil.initMdcContext(req, headers);
+
+    logger.debug("Incoming request..." + content);
+    Response response = null;
+
+    if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+      try {
+        String result = crudGraphDataService.getVertex(version, id, type);
+        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+      } catch (CrudException ce) {
+        response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+      } catch (Exception e) {
+        response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+      }
+    } else {
+      response = Response.status(Status.FORBIDDEN).entity(content)
+          .type(MediaType.APPLICATION_JSON).build();
+    }
+
+    LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+    return response;
+  }
+
+  @GET
+  @Path("/{version}/{type}/")
+  @Consumes({MediaType.APPLICATION_JSON})
+  @Produces({MediaType.APPLICATION_JSON})
+  public Response getVertices(String content, @PathParam("version") String version,
+                              @PathParam("type") String type, @PathParam("uri") @Encoded String uri,
+                              @Context HttpHeaders headers, @Context UriInfo uriInfo,
+                              @Context HttpServletRequest req) {
+
+    LoggingUtil.initMdcContext(req, headers);
+
+    logger.debug("Incoming request..." + content);
+    Response response = null;
+    if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+      Map<String, String> filter = new HashMap<String, String>();
+      for (Map.Entry<String, List<String>> e : uriInfo.getQueryParameters().entrySet()) {
+        filter.put(e.getKey(), e.getValue().get(0));
+      }
+
+      try {
+        String result = crudGraphDataService.getVertices(version, type, filter);
+        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+      } catch (CrudException ce) {
+        response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+      } catch (Exception e) {
+        response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+      }
+    } else {
+      response = Response.status(Status.FORBIDDEN).entity(content)
+          .type(MediaType.APPLICATION_JSON).build();
+    }
+
+    LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+    return response;
+  }
+
+  @GET
+  @Path("/relationships/{version}/{type}/{id}")
+  @Consumes({MediaType.APPLICATION_JSON})
+  @Produces({MediaType.APPLICATION_JSON})
+  public Response getEdge(String content, @PathParam("version") String version,
+                          @PathParam("type") String type, @PathParam("id") String id,
+                          @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+                          @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+    LoggingUtil.initMdcContext(req, headers);
+
+    logger.debug("Incoming request..." + content);
+    Response response = null;
+
+    if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+      try {
+
+        String result = crudGraphDataService.getEdge(version, id, type);
+        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+      } catch (CrudException ce) {
+        response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+      } catch (Exception e) {
+        response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+      }
+    } else {
+      response = Response.status(Status.FORBIDDEN).entity(content)
+          .type(MediaType.APPLICATION_JSON).build();
+    }
+
+    LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+    return response;
+  }
+
+  @GET
+  @Path("/relationships/{version}/{type}/")
+  @Consumes({MediaType.APPLICATION_JSON})
+  @Produces({MediaType.APPLICATION_JSON})
+  public Response getEdges(String content, @PathParam("version") String version,
+                           @PathParam("type") String type, @PathParam("uri") @Encoded String uri,
+                           @Context HttpHeaders headers, @Context UriInfo uriInfo,
+                           @Context HttpServletRequest req) {
+
+    LoggingUtil.initMdcContext(req, headers);
+
+    logger.debug("Incoming request..." + content);
+    Response response = null;
+
+    if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+      Map<String, String> filter = new HashMap<String, String>();
+      for (Map.Entry<String, List<String>> e : uriInfo.getQueryParameters().entrySet()) {
+        filter.put(e.getKey(), e.getValue().get(0));
+      }
+
+      try {
+        String result = crudGraphDataService.getEdges(version, type, filter);
+        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+      } catch (CrudException ce) {
+        response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+      } catch (Exception e) {
+        response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+      }
+    } else {
+      response = Response.status(Status.FORBIDDEN).entity(content)
+          .type(MediaType.APPLICATION_JSON).build();
+
+    }
+
+    LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+    return response;
+  }
+
+  @PUT
+  @Path("/relationships/{version}/{type}/{id}")
+  @Consumes({MediaType.APPLICATION_JSON})
+  @Produces({MediaType.APPLICATION_JSON})
+  public Response updateEdge(String content, @PathParam("version") String version,
+                             @PathParam("type") String type, @PathParam("id") String id,
+                             @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+                             @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+
+    LoggingUtil.initMdcContext(req, headers);
+
+    logger.debug("Incoming request..." + content);
+    Response response = null;
+
+    if (validateRequest(req, uri, content, Action.PUT, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+      try {
+        EdgePayload payload = EdgePayload.fromJson(content);
+        if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
+          throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
+        }
+        if (payload.getId() != null && !payload.getId().equals(id)) {
+          throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
+        }
+        String result;
+
+        if (headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) != null
+            && headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE)
+            .equalsIgnoreCase("PATCH")) {
+          result = crudGraphDataService.patchEdge(version, id, type, payload);
+        } else {
+
+          result = crudGraphDataService.updateEdge(version, id, type, payload);
+        }
+
+        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+      } catch (CrudException ce) {
+        response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+      } catch (Exception e) {
+        response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+      }
+    } else {
+      response = Response.status(Status.FORBIDDEN).entity(content)
+          .type(MediaType.APPLICATION_JSON).build();
+
+    }
+
+    LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+    return response;
+  }
+
+  @PATCH
+  @Path("/relationships/{version}/{type}/{id}")
+  @Consumes({"application/merge-patch+json"})
+  @Produces({MediaType.APPLICATION_JSON})
+  public Response patchEdge(String content, @PathParam("version") String version,
+                            @PathParam("type") String type, @PathParam("id") String id,
+                            @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+                            @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+
+    LoggingUtil.initMdcContext(req, headers);
+
+    logger.debug("Incoming request..." + content);
+    Response response = null;
+    if (validateRequest(req, uri, content, Action.PATCH,
+        CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+      try {
+        EdgePayload payload = EdgePayload.fromJson(content);
+        if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
+          throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
+        }
+        if (payload.getId() != null && !payload.getId().equals(id)) {
+          throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
+        }
+
+        String result = crudGraphDataService.patchEdge(version, id, type, payload);
+        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+      } catch (CrudException ce) {
+        response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+      } catch (Exception e) {
+        response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+      }
+    } else {
+      response = Response.status(Status.FORBIDDEN).entity(content)
+          .type(MediaType.APPLICATION_JSON).build();
+    }
+
+    LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+    return response;
+  }
+
+  @PUT
+  @Path("/{version}/{type}/{id}")
+  @Consumes({MediaType.APPLICATION_JSON})
+  @Produces({MediaType.APPLICATION_JSON})
+  public Response updateVertex(String content, @PathParam("version") String version,
+                               @PathParam("type") String type, @PathParam("id") String id,
+                               @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+                               @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+
+    LoggingUtil.initMdcContext(req, headers);
+
+    logger.debug("Incoming request..." + content);
+    Response response = null;
+
+    if (validateRequest(req, uri, content, Action.PUT, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+      try {
+        VertexPayload payload = VertexPayload.fromJson(content);
+        if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
+          throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
+        }
+        if (payload.getId() != null && !payload.getId().equals(id)) {
+          throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
+        }
+        String result;
+        if (headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) != null
+            && headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE)
+            .equalsIgnoreCase("PATCH")) {
+          result = crudGraphDataService.patchVertex(version, id, type, payload);
+        } else {
+
+          result = crudGraphDataService.updateVertex(version, id, type, payload);
+        }
+        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+      } catch (CrudException ce) {
+        response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+      } catch (Exception e) {
+        response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+      }
+    } else {
+      response = Response.status(Status.FORBIDDEN).entity(content)
+          .type(MediaType.APPLICATION_JSON).build();
+    }
+
+    LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+    return response;
+  }
+
+  @PATCH
+  @Path("/{version}/{type}/{id}")
+  @Consumes({"application/merge-patch+json"})
+  @Produces({MediaType.APPLICATION_JSON})
+  public Response patchVertex(String content, @PathParam("version") String version,
+                              @PathParam("type") String type, @PathParam("id") String id,
+                              @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+                              @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+
+    LoggingUtil.initMdcContext(req, headers);
+
+    logger.debug("Incoming request..." + content);
+    Response response = null;
+
+    if (validateRequest(req, uri, content, Action.PATCH,
+        CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+      try {
+        VertexPayload payload = VertexPayload.fromJson(content);
+        if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
+          throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
+        }
+        if (payload.getId() != null && !payload.getId().equals(id)) {
+          throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
+        }
+
+        String result = crudGraphDataService.patchVertex(version, id, type, payload);
+        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+      } catch (CrudException ce) {
+        response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+      } catch (Exception e) {
+        response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+      }
+    } else {
+      response = Response.status(Status.FORBIDDEN).entity(content)
+          .type(MediaType.APPLICATION_JSON).build();
+    }
+
+    LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+    return response;
+  }
+
+  @POST
+  @Path("/{version}/{type}/")
+  @Consumes({MediaType.APPLICATION_JSON})
+  @Produces({MediaType.APPLICATION_JSON})
+  public Response addVertex(String content, @PathParam("version") String version,
+                            @PathParam("type") String type, @PathParam("uri") @Encoded String uri,
+                            @Context HttpHeaders headers, @Context UriInfo uriInfo,
+                            @Context HttpServletRequest req) {
+
+    LoggingUtil.initMdcContext(req, headers);
+
+    logger.debug("Incoming request..." + content);
+    Response response = null;
+
+    if (validateRequest(req, uri, content, Action.POST,
+        CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+      try {
+        VertexPayload payload = VertexPayload.fromJson(content);
+        if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
+          throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
+        }
+        if (payload.getId() != null) {
+          throw new CrudException("ID specified , use Http PUT to update Vertex",
+              Status.BAD_REQUEST);
+        }
+
+        if (payload.getType() != null && !payload.getType().equals(type)) {
+          throw new CrudException("Vertex Type mismatch", Status.BAD_REQUEST);
+        }
+
+        String result = crudGraphDataService.addVertex(version, type, payload);
+        response = Response.status(Status.CREATED).entity(result).type(mediaType).build();
+      } catch (CrudException ce) {
+        response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+      } catch (Exception e) {
+        response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+      }
+    } else {
+      response = Response.status(Status.FORBIDDEN).entity(content)
+          .type(MediaType.APPLICATION_JSON).build();
+    }
+
+    LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+    return response;
+  }
+
+  @POST
+  @Path("/{version}/")
+  @Consumes({MediaType.APPLICATION_JSON})
+  @Produces({MediaType.APPLICATION_JSON})
+  public Response addVertex(String content, @PathParam("version") String version,
+                            @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+                            @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+
+    LoggingUtil.initMdcContext(req, headers);
+
+    logger.debug("Incoming request..." + content);
+    Response response = null;
+
+    if (validateRequest(req, uri, content, Action.POST,
+        CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+      try {
+
+        VertexPayload payload = VertexPayload.fromJson(content);
+        if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
+          throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
+        }
+        if (payload.getId() != null) {
+          throw new CrudException("ID specified , use Http PUT to update Vertex",
+              Status.BAD_REQUEST);
+        }
+
+        if (payload.getType() == null || payload.getType().isEmpty()) {
+          throw new CrudException("Missing Vertex Type ", Status.BAD_REQUEST);
+        }
+        String result = crudGraphDataService.addVertex(version, payload.getType(), payload);
+        response = Response.status(Status.CREATED).entity(result).type(mediaType).build();
+      } catch (CrudException ce) {
+        response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+      } catch (Exception e) {
+        response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+      }
+    } else {
+      response = Response.status(Status.FORBIDDEN).entity(content)
+          .type(MediaType.APPLICATION_JSON).build();
+    }
+
+    LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+    return response;
+  }
+
+  @POST
+  @Path("/relationships/{version}/{type}/")
+  @Consumes({MediaType.APPLICATION_JSON})
+  @Produces({MediaType.APPLICATION_JSON})
+  public Response addEdge(String content, @PathParam("version") String version,
+                          @PathParam("type") String type, @PathParam("uri") @Encoded String uri,
+                          @Context HttpHeaders headers, @Context UriInfo uriInfo,
+                          @Context HttpServletRequest req) {
+
+    LoggingUtil.initMdcContext(req, headers);
+
+    logger.debug("Incoming request..." + content);
+    Response response = null;
+
+    if (validateRequest(req, uri, content, Action.POST,
+        CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+
+      try {
+        EdgePayload payload = EdgePayload.fromJson(content);
+        if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
+          throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
+        }
+        if (payload.getId() != null) {
+          throw new CrudException("ID specified , use Http PUT to update Edge", Status.BAD_REQUEST);
+        }
+
+        if (payload.getType() != null && !payload.getType().equals(type)) {
+          throw new CrudException("Edge Type mismatch", Status.BAD_REQUEST);
+        }
+        String result = crudGraphDataService.addEdge(version, type, payload);
+        response = Response.status(Status.CREATED).entity(result).type(mediaType).build();
+      } catch (CrudException ce) {
+        response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+      } catch (Exception e) {
+        response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+      }
+    } else {
+      response = Response.status(Status.FORBIDDEN).entity(content)
+          .type(MediaType.APPLICATION_JSON).build();
+    }
+
+    LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+    return response;
+  }
+
+  @POST
+  @Path("/relationships/{version}/")
+  @Consumes({MediaType.APPLICATION_JSON})
+  @Produces({MediaType.APPLICATION_JSON})
+  public Response addEdge(String content, @PathParam("version") String version,
+                          @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+                          @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+
+    LoggingUtil.initMdcContext(req, headers);
+
+    logger.debug("Incoming request..." + content);
+    Response response = null;
+
+    if (validateRequest(req, uri, content, Action.POST,
+        CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+
+      try {
+        EdgePayload payload = EdgePayload.fromJson(content);
+        if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
+          throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
+        }
+        if (payload.getId() != null) {
+          throw new CrudException("ID specified , use Http PUT to update Edge", Status.BAD_REQUEST);
+        }
+
+        if (payload.getType() == null || payload.getType().isEmpty()) {
+          throw new CrudException("Missing Edge Type ", Status.BAD_REQUEST);
+        }
+        String result = crudGraphDataService.addEdge(version, payload.getType(), payload);
+
+        response = Response.status(Status.CREATED).entity(result).type(mediaType).build();
+      } catch (CrudException ce) {
+        response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+      } catch (Exception e) {
+        response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+      }
+    } else {
+      response = Response.status(Status.FORBIDDEN).entity(content)
+          .type(MediaType.APPLICATION_JSON).build();
+    }
+
+    LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+    return response;
+  }
+
+  @DELETE
+  @Path("/{version}/{type}/{id}")
+  @Consumes({MediaType.APPLICATION_JSON})
+  @Produces({MediaType.APPLICATION_JSON})
+  public Response deleteVertex(String content, @PathParam("version") String version,
+                               @PathParam("type") String type, @PathParam("id") String id,
+                               @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+                               @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+
+    LoggingUtil.initMdcContext(req, headers);
+
+    logger.debug("Incoming request..." + content);
+    Response response = null;
+
+    if (validateRequest(req, uri, content, Action.DELETE,
+        CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+
+      try {
+        String result = crudGraphDataService.deleteVertex(version, id, type);
+        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+      } catch (CrudException ce) {
+        response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+      } catch (Exception e) {
+        response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+      }
+    } else {
+      response = Response.status(Status.FORBIDDEN).entity(content)
+          .type(MediaType.APPLICATION_JSON).build();
+    }
+
+    LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+    return response;
+  }
+
+  @DELETE
+  @Path("/relationships/{version}/{type}/{id}")
+  @Consumes({MediaType.APPLICATION_JSON})
+  @Produces({MediaType.APPLICATION_JSON})
+  public Response deleteEdge(String content, @PathParam("version") String version,
+                             @PathParam("type") String type, @PathParam("id") String id,
+                             @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+                             @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+
+    LoggingUtil.initMdcContext(req, headers);
+
+    logger.debug("Incoming request..." + content);
+    Response response = null;
+    if (validateRequest(req, uri, content, Action.DELETE,
+        CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+      try {
+        String result = crudGraphDataService.deleteEdge(version, id, type);
+        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+      } catch (CrudException ce) {
+        response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+      } catch (Exception e) {
+        response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+      }
+    } else {
+      response = Response.status(Status.FORBIDDEN).entity(content)
+          .type(MediaType.APPLICATION_JSON).build();
+    }
+
+    LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+    return response;
+  }
+
+  protected boolean validateRequest(HttpServletRequest req, String uri, String content,
+                                    Action action, String authPolicyFunctionName) {
+    try {
+      String cipherSuite = (String) req.getAttribute("javax.servlet.request.cipher_suite");
+      String authUser = null;
+      if (cipherSuite != null) {
+        X509Certificate[] certChain = (X509Certificate[]) req
+            .getAttribute("javax.servlet.request.X509Certificate");
+        X509Certificate clientCert = certChain[0];
+        X500Principal subjectDn = clientCert.getSubjectX500Principal();
+        authUser = subjectDn.toString();
+      }
+      return this.auth.validateRequest(authUser.toLowerCase(), action.toString()
+          + ":" + authPolicyFunctionName);
+    } catch (Exception e) {
+      logResult(action, uri, e);
+      return false;
+    }
+  }
+
+  void logResult(Action op, String uri, Exception e) {
+
+    logger.error(CrudServiceMsgs.EXCEPTION_DURING_METHOD_CALL, op.toString(), uri,
+        e.getStackTrace().toString());
+
+    // Clear the MDC context so that no other transaction inadvertently
+    // uses our transaction id.
+    MDC.clear();
+  }
+}
diff --git a/src/main/java/org/openecomp/crud/service/EdgePayload.java b/src/main/java/org/openecomp/crud/service/EdgePayload.java
new file mode 100644 (file)
index 0000000..d098d16
--- /dev/null
@@ -0,0 +1,115 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.service;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+
+import org.openecomp.crud.exception.CrudException;
+
+import javax.ws.rs.core.Response.Status;
+
+public class EdgePayload {
+
+  private String id;
+  private String type;
+  private String url;
+  private String source;
+  private String target;
+  private JsonElement properties;
+
+  private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
+
+
+  @Override
+  public String toString() {
+    return "EdgePayload [id=" + id + ", type=" + type + ", url=" + url + ", source="
+        + source + ", target=" + target + ", properties=" + properties + "]";
+  }
+
+  public String toJson() {
+    return gson.toJson(this);
+  }
+
+  public static EdgePayload fromJson(String payload) throws CrudException {
+    try {
+      if (payload == null || payload.isEmpty()) {
+        throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST);
+      }
+      return gson.fromJson(payload, EdgePayload.class);
+    } catch (Exception ex) {
+      throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST);
+    }
+  }
+
+  public String getId() {
+    return id;
+  }
+
+  public void setId(String id) {
+    this.id = id;
+  }
+
+  public String getType() {
+    return type;
+  }
+
+  public void setType(String type) {
+    this.type = type;
+  }
+
+  public String getUrl() {
+    return url;
+  }
+
+  public void setUrl(String url) {
+    this.url = url;
+  }
+
+  public String getSource() {
+    return source;
+  }
+
+  public void setSource(String source) {
+    this.source = source;
+  }
+
+  public String getTarget() {
+    return target;
+  }
+
+  public void setTarget(String target) {
+    this.target = target;
+  }
+
+  public JsonElement getProperties() {
+    return properties;
+  }
+
+  public void setProperties(JsonElement properties) {
+    this.properties = properties;
+  }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/openecomp/crud/service/JaxrsEchoService.java b/src/main/java/org/openecomp/crud/service/JaxrsEchoService.java
new file mode 100644 (file)
index 0000000..0edd316
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.service;
+
+import org.openecomp.cl.api.Logger;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.crud.logging.LoggingUtil;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriInfo;
+
+
+public class JaxrsEchoService {
+
+  private static Logger logger = LoggerFactory.getInstance()
+      .getLogger(JaxrsEchoService.class.getName());
+  private static Logger auditLogger = LoggerFactory.getInstance()
+      .getAuditLogger(JaxrsEchoService.class.getName());
+
+  @GET
+  @Path("echo/{input}")
+  @Produces("text/plain")
+  public String ping(@PathParam("input") String input,
+                     @Context HttpHeaders headers,
+                     @Context UriInfo info,
+                     @Context HttpServletRequest req) {
+
+    LoggingUtil.initMdcContext(req, headers);
+    LoggingUtil.logRestRequest(logger, auditLogger, req, Response.status(Status.OK)
+        .entity("OK").build());
+
+    return "Hello, " + input + ".";
+  }
+}
\ No newline at end of file
diff --git a/src/main/java/org/openecomp/crud/service/VertexPayload.java b/src/main/java/org/openecomp/crud/service/VertexPayload.java
new file mode 100644 (file)
index 0000000..ed79002
--- /dev/null
@@ -0,0 +1,117 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.service;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+
+import org.openecomp.crud.exception.CrudException;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.ws.rs.core.Response.Status;
+
+public class VertexPayload {
+
+  private String id;
+  private String type;
+  private String url;
+  private JsonElement properties;
+  private List<EdgePayload> in = new ArrayList<EdgePayload>();
+  private List<EdgePayload> out = new ArrayList<EdgePayload>();
+
+  private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
+
+  public String toJson() {
+    return gson.toJson(this);
+  }
+
+  public static VertexPayload fromJson(String payload) throws CrudException {
+    try {
+      if (payload == null || payload.isEmpty()) {
+        throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST);
+      }
+      return gson.fromJson(payload, VertexPayload.class);
+    } catch (Exception ex) {
+      throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST);
+    }
+  }
+
+  public String getId() {
+    return id;
+  }
+
+  public void setId(String id) {
+    this.id = id;
+  }
+
+  public String getType() {
+    return type;
+  }
+
+  public void setType(String type) {
+    this.type = type;
+  }
+
+  public String getUrl() {
+    return url;
+  }
+
+  public void setUrl(String url) {
+    this.url = url;
+  }
+
+  public JsonElement getProperties() {
+    return properties;
+  }
+
+  public void setProperties(JsonElement properties) {
+    this.properties = properties;
+  }
+
+  public List<EdgePayload> getIn() {
+    return in;
+  }
+
+  public void setIn(List<EdgePayload> in) {
+    this.in = in;
+  }
+
+  public List<EdgePayload> getOut() {
+    return out;
+  }
+
+  public void setOut(List<EdgePayload> out) {
+    this.out = out;
+  }
+
+
+  @Override
+  public String toString() {
+    return "VertexPayload [id=" + id + ", type=" + type + ", url=" + url + ", properties="
+        + properties + ", in=" + in + ", out=" + out + "]";
+  }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/openecomp/crud/util/CrudJaxbTransformation.java b/src/main/java/org/openecomp/crud/util/CrudJaxbTransformation.java
new file mode 100644 (file)
index 0000000..c1a1e18
--- /dev/null
@@ -0,0 +1,92 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.util;
+
+import org.eclipse.persistence.dynamic.DynamicEntity;
+import org.eclipse.persistence.jaxb.JAXBMarshaller;
+import org.eclipse.persistence.jaxb.MarshallerProperties;
+import org.eclipse.persistence.jaxb.UnmarshallerProperties;
+import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import javax.ws.rs.core.MediaType;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.stream.StreamSource;
+
+public class CrudJaxbTransformation {
+  /**
+   * Marshal a dynamic entity into a string.
+   *
+   * @param entity      the dynamic entity
+   * @param jaxbContext the dynamic jaxb context
+   * @return the marshaled entity
+   * @throws RouterException on error
+   */
+  public static String marshal(MediaType mediaType, final DynamicEntity entity,
+                               final DynamicJAXBContext jaxbContext) throws JAXBException {
+
+    final JAXBMarshaller marshaller = jaxbContext.createMarshaller();
+    marshaller.setProperty(JAXBMarshaller.JAXB_FORMATTED_OUTPUT, false);
+
+    if (MediaType.APPLICATION_JSON_TYPE.isCompatible(mediaType)) {
+      marshaller.setProperty("eclipselink.media-type", "application/json");
+      marshaller.setProperty("eclipselink.json.include-root", false);
+      marshaller.setProperty(MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, Boolean.FALSE);
+    }
+
+    final StringWriter writer = new StringWriter();
+    marshaller.marshal(entity, writer);
+    return writer.toString();
+
+  }
+
+  /**
+   * @param type
+   * @param json
+   * @param mediaType
+   * @return
+   * @throws JAXBException
+   * @throws Exception
+   */
+  public static Object unmarshal(String javaClass, String content, MediaType mediaType,
+                                 final DynamicJAXBContext jaxbContext) throws JAXBException {
+    Object clazz = null;
+    Unmarshaller unmarshaller = null;
+
+    clazz = jaxbContext.newDynamicEntity(javaClass);
+
+    unmarshaller = jaxbContext.createUnmarshaller();
+    if (mediaType.equals(MediaType.APPLICATION_JSON_TYPE)) {
+      unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json");
+      unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false);
+      unmarshaller.setProperty(UnmarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, true);
+    }
+
+    return unmarshaller.unmarshal(new StreamSource(new StringReader(content)),
+        clazz.getClass()).getValue();
+  }
+
+}
diff --git a/src/main/java/org/openecomp/crud/util/CrudProperties.java b/src/main/java/org/openecomp/crud/util/CrudProperties.java
new file mode 100644 (file)
index 0000000..69b2e16
--- /dev/null
@@ -0,0 +1,77 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+public class CrudProperties {
+
+  private static Properties properties;
+
+  static {
+    properties = new Properties();
+    File file = new File(CrudServiceConstants.CRD_CONFIG_FILE);
+    try {
+      properties.load(new FileInputStream(file));
+    } catch (FileNotFoundException e) {
+      e.printStackTrace();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  public static String get(String key) {
+    return properties.getProperty(key);
+  }
+
+  public static String get(String key, String defaultValue) {
+    return properties.getProperty(key, defaultValue);
+  }
+
+  public static void put(String key, String value) {
+    properties.setProperty(key, value);
+    FileOutputStream fileOut = null;
+    try {
+      fileOut = new FileOutputStream(new File(CrudServiceConstants.CRD_CONFIG_FILE));
+      properties.store(fileOut, "Added property: " + key);
+    } catch (Exception e) {
+      e.printStackTrace();
+    } finally {
+
+      try {
+        fileOut.close();
+      } catch (IOException ex) {
+        ex.printStackTrace();
+      }
+    }
+
+  }
+
+
+}
diff --git a/src/main/java/org/openecomp/crud/util/CrudServiceConstants.java b/src/main/java/org/openecomp/crud/util/CrudServiceConstants.java
new file mode 100644 (file)
index 0000000..9543e2d
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.util;
+
+public class CrudServiceConstants {
+  public static final String CRD_SERVICE_NAME = "Crud-Service";
+
+  public static final String CRD_FILESEP = (System.getProperty("file.separator") == null) ? "/"
+      : System.getProperty("file.separator");
+
+  public static final String CRD_SPECIFIC_CONFIG = System.getProperty("CONFIG_HOME") + CRD_FILESEP;
+
+  public static final String CRD_HOME_MODEL = CRD_SPECIFIC_CONFIG + "model" + CRD_FILESEP;
+  public static final String CRD_HOME_AUTH = CRD_SPECIFIC_CONFIG + "auth" + CRD_FILESEP;
+
+  public static final String CRD_GRAPH_HOST = "crud.graph.host";
+  public static final String CRD_GRAPH_PORT = "crud.graph.port";
+  public static final String CRD_GRAPH_NAME = "crud.graph.name";
+  public static final String CRD_STORAGE_BACKEND_DB = "crud.storage.backend.db";
+  public static final String CRD_HBASE_ZNODE_PARENT
+      = "crud.storage.hbase.ext.zookeeper.znode.parent";
+
+  public static final String CRD_CONFIG_FILE = CRD_SPECIFIC_CONFIG + "crud-api.properties";
+  public static final String CRD_AUTH_FILE = CRD_HOME_AUTH + "crud_policy.json";
+
+  public static final String CRD_AUTH_POLICY_NAME = "crud";
+
+  public static final String CRD_EVENT_STREAM_HOSTS = "event.stream.hosts";
+
+
+
+}
diff --git a/src/main/java/org/openecomp/crud/util/CrudServiceUtil.java b/src/main/java/org/openecomp/crud/util/CrudServiceUtil.java
new file mode 100644 (file)
index 0000000..25e3de6
--- /dev/null
@@ -0,0 +1,56 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.util;
+
+import org.openecomp.crud.exception.CrudException;
+
+import javax.ws.rs.core.Response.Status;
+
+public class CrudServiceUtil {
+
+
+  public static Object validateFieldType(String value, Class clazz) throws CrudException {
+    try {
+      if (clazz.isAssignableFrom(Integer.class)) {
+        return Integer.parseInt(value);
+      } else if (clazz.isAssignableFrom(Long.class)) {
+        return Long.parseLong(value);
+      } else if (clazz.isAssignableFrom(Float.class)) {
+        return Float.parseFloat(value);
+      } else if (clazz.isAssignableFrom(Double.class)) {
+        return Double.parseDouble(value);
+      } else if (clazz.isAssignableFrom(Boolean.class)) {
+        if (!value.equals("true") && !value.equals("false")) {
+          throw new CrudException("Invalid propertry value: " + value, Status.BAD_REQUEST);
+        }
+        return Boolean.parseBoolean(value);
+      } else {
+        return value;
+      }
+    } catch (Exception e) {
+      throw new CrudException("Invalid property value: " + value, Status.BAD_REQUEST);
+    }
+  }
+
+}
diff --git a/src/main/java/org/openecomp/crud/util/FileWatcher.java b/src/main/java/org/openecomp/crud/util/FileWatcher.java
new file mode 100644 (file)
index 0000000..8c7cbc1
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.crud.util;
+
+import java.io.File;
+import java.util.TimerTask;
+
+public abstract class FileWatcher extends TimerTask {
+  private long timeStamp;
+  private File file;
+
+  public FileWatcher(File file) {
+    this.file = file;
+    this.timeStamp = file.lastModified();
+  }
+
+  public final void run() {
+    long timeStamp = file.lastModified();
+
+    if ((timeStamp - this.timeStamp) > 500) {
+      this.timeStamp = timeStamp;
+      onChange(file);
+    }
+  }
+
+  protected abstract void onChange(File file);
+}
\ No newline at end of file
diff --git a/src/main/java/org/openecomp/schema/OxmModelLoader.java b/src/main/java/org/openecomp/schema/OxmModelLoader.java
new file mode 100644 (file)
index 0000000..4ef77a2
--- /dev/null
@@ -0,0 +1,168 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.schema;
+
+import org.eclipse.persistence.jaxb.JAXBContextProperties;
+import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
+import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.logging.CrudServiceMsgs;
+import org.openecomp.crud.util.CrudServiceConstants;
+import org.openecomp.crud.util.FileWatcher;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.ws.rs.core.Response.Status;
+import javax.xml.bind.JAXBException;
+
+
+public class OxmModelLoader {
+
+  private static Map<String, DynamicJAXBContext> versionContextMap
+      = new ConcurrentHashMap<String, DynamicJAXBContext>();
+  private static Map<String, Timer> timers = new ConcurrentHashMap<String, Timer>();
+
+  final static Pattern p = Pattern.compile("aai_oxm_(.*).xml");
+
+  private static org.openecomp.cl.api.Logger logger = LoggerFactory.getInstance()
+      .getLogger(OxmModelLoader.class.getName());
+
+  public synchronized static void loadModels() throws CrudException {
+    ClassLoader cl = OxmModelLoader.class.getClassLoader();
+    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
+    Resource[] resources;
+    try {
+      resources = resolver.getResources("classpath*:/oxm/aai_oxm*.xml");
+    } catch (IOException ex) {
+      logger.error(CrudServiceMsgs.OXM_LOAD_ERROR, ex.getMessage());
+      throw new CrudException("", Status.NOT_FOUND);
+    }
+
+    if (resources.length == 0) {
+      logger.error(CrudServiceMsgs.OXM_LOAD_ERROR, "No OXM schema files found on classpath");
+      throw new CrudException("Failed to load schema", Status.NOT_FOUND);
+    }
+
+    for (Resource resource : resources) {
+      Matcher matcher = p.matcher(resource.getFilename());
+
+      if (matcher.matches()) {
+        try {
+          OxmModelLoader.loadModel(matcher.group(1), resource);
+        } catch (Exception e) {
+          logger.error(CrudServiceMsgs.OXM_LOAD_ERROR, "Failed to load " + resource.getFilename()
+              + ": " + e.getMessage());
+          throw new CrudException("Failed to load schema", Status.NOT_FOUND);
+        }
+      }
+    }
+  }
+
+  private static void addtimer(String version, File file) {
+    TimerTask task = null;
+    task = new FileWatcher(
+        file) {
+      protected void onChange(File file) {
+        // here we implement the onChange
+        logger.info(CrudServiceMsgs.OXM_FILE_CHANGED, file.getName());
+
+        try {
+          OxmModelLoader.loadModel(version, file);
+        } catch (Exception e) {
+          e.printStackTrace();
+        }
+
+      }
+    };
+
+    if (!timers.containsKey(version)) {
+      Timer timer = new Timer("oxm-" + version);
+      timer.schedule(task, new Date(), 10000);
+      timers.put(version, timer);
+
+    }
+  }
+
+  private synchronized static void loadModel(String version, File file)
+      throws JAXBException, IOException {
+    InputStream inputStream = new FileInputStream(file);
+    loadModel(version, file.getName(), inputStream);
+    addtimer(version, file);
+  }
+
+  private synchronized static void loadModel(String version, Resource resource)
+      throws JAXBException, IOException {
+    InputStream inputStream = resource.getInputStream();
+    loadModel(version, resource.getFilename(), inputStream);
+  }
+
+  private synchronized static void loadModel(String version, String resourceName,
+                                             InputStream inputStream)
+      throws JAXBException, IOException {
+    Map<String, Object> properties = new HashMap<String, Object>();
+    properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, inputStream);
+    final DynamicJAXBContext jaxbContext = DynamicJAXBContextFactory
+        .createContextFromOXM(Thread.currentThread().getContextClassLoader(), properties);
+    versionContextMap.put(version, jaxbContext);
+    logger.info(CrudServiceMsgs.LOADED_OXM_FILE, resourceName);
+  }
+
+  public static DynamicJAXBContext getContextForVersion(String version) throws CrudException {
+    if (versionContextMap == null || versionContextMap.isEmpty()) {
+      loadModels();
+    } else if (!versionContextMap.containsKey(version)) {
+      try {
+        loadModel(version, new File(CrudServiceConstants.CRD_HOME_MODEL + "aai_oxm_"
+            + version + ".xml"));
+      } catch (Exception e) {
+        throw new CrudException("", Status.NOT_FOUND);
+      }
+    }
+
+    return versionContextMap.get(version);
+  }
+
+  public static Map<String, DynamicJAXBContext> getVersionContextMap() {
+    return versionContextMap;
+  }
+
+  public static void setVersionContextMap(Map<String, DynamicJAXBContext> versionContextMap) {
+    OxmModelLoader.versionContextMap = versionContextMap;
+  }
+
+}
diff --git a/src/main/java/org/openecomp/schema/OxmModelValidator.java b/src/main/java/org/openecomp/schema/OxmModelValidator.java
new file mode 100644 (file)
index 0000000..45a2597
--- /dev/null
@@ -0,0 +1,325 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.schema;
+
+import com.google.common.base.CaseFormat;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+
+import org.eclipse.persistence.dynamic.DynamicType;
+import org.eclipse.persistence.internal.helper.DatabaseField;
+import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
+import org.eclipse.persistence.mappings.DatabaseMapping;
+import org.eclipse.persistence.oxm.XMLField;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.util.CrudServiceUtil;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import javax.ws.rs.core.Response.Status;
+
+public class OxmModelValidator {
+  public enum Metadata {
+    NODE_TYPE("aai-node-type"),
+    URI("aai-uri"),
+    CREATED_TS("aai-created-ts"),
+    SOT("source-of-truth"),
+    LAST_MOD_SOT("last-mod-source-of-truth");
+
+    private final String propName;
+
+    Metadata(String propName) {
+      this.propName = propName;
+    }
+
+    public String propertyName() {
+      return propName;
+    }
+
+    public static boolean isProperty(String property) {
+      for (Metadata meta : Metadata.values()) {
+        if (meta.propName.equals(property)) {
+          return true;
+        }
+      }
+      return false;
+    }
+  }
+
+
+  public static Map<String, Object> resolveCollectionfilter(String version, String type,
+                                                            Map<String, String> filter)
+      throws CrudException {
+
+    DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
+
+    Map<String, Object> result = new HashMap<String, Object>();
+    if (jaxbContext == null) {
+      throw new CrudException("", Status.NOT_FOUND);
+    }
+    final DynamicType modelObjectType = jaxbContext.getDynamicType(CaseFormat.LOWER_CAMEL
+        .to(CaseFormat.UPPER_CAMEL,
+        CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type)));
+
+    for (String key : filter.keySet()) {
+      String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, key);
+      if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) != null) {
+        try {
+          DatabaseMapping mapping = modelObjectType.getDescriptor()
+              .getMappingForAttributeName(keyJavaName);
+          Object value = CrudServiceUtil.validateFieldType(filter.get(key),
+              mapping.getField().getType());
+          result.put(key, value);
+        } catch (Exception ex) {
+          // Skip any exceptions thrown while validating the filter
+          // key value
+          continue;
+        }
+      }
+    }
+
+    return result;
+
+  }
+
+  public static String resolveCollectionType(String version, String type) throws CrudException {
+
+    DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
+
+    if (jaxbContext == null) {
+      throw new CrudException("", Status.NOT_FOUND);
+    }
+    // Determine if the Object part is a collection type in the model
+    // definition
+    final DynamicType modelObjectType = jaxbContext.getDynamicType(CaseFormat.LOWER_CAMEL
+        .to(CaseFormat.UPPER_CAMEL,
+        CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type)));
+
+    if (modelObjectType == null) {
+      throw new CrudException("", Status.NOT_FOUND);
+    }
+
+    if (modelObjectType.getDescriptor().getMappings().size() == 1
+        && modelObjectType.getDescriptor().getMappings().get(0).isCollectionMapping()) {
+      String childJavaObjectName = modelObjectType.getDescriptor().getMappings()
+          .get(0).getAttributeName();
+      childJavaObjectName = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, childJavaObjectName);
+      final DynamicType childObjectType = jaxbContext.getDynamicType(childJavaObjectName);
+      if (childObjectType == null) {
+        // Should not happen as child object is defined in oxm model
+        // itself
+        throw new CrudException("", Status.NOT_FOUND);
+      }
+      return childObjectType.getDescriptor().getTableName();
+    } else {
+      return modelObjectType.getDescriptor().getTableName();
+    }
+
+  }
+
+
+  public static Vertex validateIncomingUpsertPayload(String id, String version, String type,
+                                                     JsonElement properties)
+      throws CrudException {
+
+    try {
+      type = resolveCollectionType(version, type);
+      DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
+      String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL,
+          CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type));
+
+      final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass);
+
+      Set<Map.Entry<String, JsonElement>> payloadEntriesSet = properties.getAsJsonObject()
+          .entrySet();
+
+      //loop through input to validate against schema
+      for (Map.Entry<String, JsonElement> entry : payloadEntriesSet) {
+        String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, entry.getKey());
+
+        // check for valid field
+        if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) == null) {
+          throw new CrudException("Invalid field: " + entry.getKey(), Status.BAD_REQUEST);
+        }
+
+      }
+
+      Map<String, JsonElement> entriesMap = new HashMap<String, JsonElement>();
+      for (Map.Entry<String, JsonElement> entry : payloadEntriesSet) {
+        entriesMap.put(entry.getKey(), entry.getValue());
+      }
+
+      Vertex.Builder modelVertexBuilder = new Vertex.Builder(type);
+      if (id != null) {
+        modelVertexBuilder.id(id);
+      }
+      for (DatabaseMapping mapping : modelObjectType.getDescriptor().getMappings()) {
+        if (mapping.isAbstractDirectMapping()) {
+          DatabaseField field = mapping.getField();
+          String defaultValue = mapping.getProperties().get("defaultValue") == null ? ""
+              : mapping.getProperties().get("defaultValue").toString();
+
+          String keyName = field.getName().substring(0, field.getName().indexOf("/"));
+
+          if (((XMLField) field).isRequired() && !entriesMap.containsKey(keyName)
+              && !defaultValue.isEmpty()) {
+            modelVertexBuilder.property(keyName,
+                CrudServiceUtil.validateFieldType(defaultValue, field.getType()));
+          }
+          // if schema field is required and not set then reject
+          if (((XMLField) field).isRequired() && !entriesMap.containsKey(keyName)
+              && defaultValue.isEmpty()) {
+            throw new CrudException("Missing required field: " + keyName, Status.BAD_REQUEST);
+          }
+          // If invalid field then reject
+          if (entriesMap.containsKey(keyName)) {
+            Object value = CrudServiceUtil.validateFieldType(entriesMap.get(keyName)
+                .getAsString(), field.getType());
+            modelVertexBuilder.property(keyName, value);
+          }
+
+          // Set defaults
+          if (!defaultValue.isEmpty() && !entriesMap.containsKey(keyName)) {
+            modelVertexBuilder.property(keyName,
+                CrudServiceUtil.validateFieldType(defaultValue, field.getType()));
+          }
+        }
+      }
+
+      return modelVertexBuilder.build();
+    } catch (Exception e) {
+      throw new CrudException(e.getMessage(), Status.BAD_REQUEST);
+    }
+  }
+
+  public static Vertex validateIncomingPatchPayload(String id, String version, String type,
+                                                    JsonElement properties, Vertex existingVertex)
+      throws CrudException {
+
+    try {
+      type = resolveCollectionType(version, type);
+      DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
+      String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL,
+          CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type));
+
+      final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass);
+
+      Set<Map.Entry<String, JsonElement>> payloadEntriesSet = properties.getAsJsonObject()
+          .entrySet();
+
+      // Loop through the payload properties and merge with existing
+      // vertex props
+      for (Map.Entry<String, JsonElement> entry : payloadEntriesSet) {
+
+        String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, entry.getKey());
+
+        // check for valid field
+        if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) == null) {
+          throw new CrudException("Invalid field: " + entry.getKey(), Status.BAD_REQUEST);
+        }
+
+        DatabaseField field = modelObjectType.getDescriptor()
+            .getMappingForAttributeName(keyJavaName).getField();
+        String defaultValue = modelObjectType.getDescriptor()
+            .getMappingForAttributeName(keyJavaName)
+            .getProperties().get("defaultValue") == null ? ""
+            : modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName)
+            .getProperties().get("defaultValue").toString();
+
+        // check if mandatory field is not set to null
+        if (((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull
+            && !defaultValue.isEmpty()) {
+          existingVertex.getProperties().put(entry.getKey(),
+              CrudServiceUtil.validateFieldType(defaultValue, field.getType()));
+        } else if (((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull
+            && defaultValue.isEmpty()) {
+          throw new CrudException("Mandatory field: " + entry.getKey()
+              + " can't be set to null",
+              Status.BAD_REQUEST);
+        } else if (!((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull
+            && existingVertex.getProperties().containsKey(entry.getKey())) {
+          existingVertex.getProperties().remove(entry.getKey());
+        } else if (!(entry.getValue() instanceof JsonNull)) {
+          // add/update the value if found in existing vertex
+          Object value = CrudServiceUtil.validateFieldType(entry.getValue().getAsString(),
+              field.getType());
+          existingVertex.getProperties().put(entry.getKey(), value);
+        }
+
+      }
+
+      return existingVertex;
+    } catch (Exception e) {
+      throw new CrudException(e.getMessage(), Status.BAD_REQUEST);
+    }
+
+  }
+
+  private static DatabaseField getDatabaseField(String fieldName, DynamicType modelObjectType) {
+    for (DatabaseField field : modelObjectType.getDescriptor().getAllFields()) {
+      int ix = field.getName().indexOf("/");
+      if (ix <= 0) {
+        ix = field.getName().length();
+      }
+
+      String keyName = field.getName().substring(0, ix);
+      if (fieldName.equals(keyName)) {
+        return field;
+      }
+    }
+    return null;
+  }
+
+  public static Vertex validateOutgoingPayload(String version, Vertex vertex) {
+
+    Vertex.Builder modelVertexBuilder = new Vertex.Builder(vertex.getType())
+        .id(vertex.getId().get());
+
+    try {
+      DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
+      String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL,
+          CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL,
+              vertex.getProperties().get(Metadata.NODE_TYPE.propertyName()) != null
+                  ? vertex.getProperties().get(Metadata.NODE_TYPE.propertyName()).toString() : vertex.getType()));
+      final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass);
+
+      for (String key : vertex.getProperties().keySet()) {
+        DatabaseField field = getDatabaseField(key, modelObjectType);
+        if (field != null) {
+          if (!Metadata.isProperty(key)) {
+            modelVertexBuilder.property(key, vertex.getProperties().get(key));
+          }
+        }
+      }
+      return modelVertexBuilder.build();
+    } catch (Exception ex) {
+      return vertex;
+    }
+
+  }
+
+
+}
diff --git a/src/main/java/org/openecomp/schema/RelationshipSchema.java b/src/main/java/org/openecomp/schema/RelationshipSchema.java
new file mode 100644 (file)
index 0000000..5b28e28
--- /dev/null
@@ -0,0 +1,138 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.schema;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+import org.openecomp.crud.exception.CrudException;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import javax.ws.rs.core.Response.Status;
+
+public class RelationshipSchema {
+  private static final Gson gson = new GsonBuilder().create();
+
+  public static final String SCHEMA_SOURCE_NODE_TYPE = "source-node-type";
+  public static final String SCHEMA_TARGET_NODE_TYPE = "target-node-type";
+  public static final String SCHEMA_RELATIONSHIP_TYPE = "relationship-type";
+  public static final String SCHEMA_RELATIONSHIP_TYPES_ARRAY = "relationship-types";
+  public static final String SCHEMA_RELATIONSHIP_PROPERTIES = "properties";
+  public static final String SCHEMA_RELATIONS_ARRAY = "relations";
+
+  /**
+   * key = source-node-type:target-node-type:relationship-type value = map of properties with name
+   * and type . Like propertyName:PropertyType
+   */
+  private HashMap<String, HashMap<String, Class<?>>> relations
+      = new HashMap<String, HashMap<String, Class<?>>>();
+  /**
+   * Hashmap of valid relationship types alongwith properrties.
+   */
+  private HashMap<String, HashMap<String, Class<?>>> relationTypes
+      = new HashMap<String, HashMap<String, Class<?>>>();
+
+
+  public RelationshipSchema(String json) throws CrudException {
+
+    JsonParser parser = new JsonParser();
+    try {
+      JsonObject root = parser.parse(json).getAsJsonObject();
+      JsonArray relationshipTypesArray = root.getAsJsonArray(SCHEMA_RELATIONSHIP_TYPES_ARRAY);
+      JsonArray relationsArray = root.getAsJsonArray(SCHEMA_RELATIONS_ARRAY);
+
+      //First load all the relationship-types
+      for (JsonElement item : relationshipTypesArray) {
+        JsonObject obj = item.getAsJsonObject();
+        String type = obj.get(SCHEMA_RELATIONSHIP_TYPE).getAsString();
+
+
+        HashMap<String, Class<?>> props = new HashMap<String, Class<?>>();
+        Set<Map.Entry<String, JsonElement>> entries = obj.get(SCHEMA_RELATIONSHIP_PROPERTIES)
+            .getAsJsonObject().entrySet();
+
+        for (Map.Entry<String, JsonElement> entry : entries) {
+          props.put(entry.getKey(), resolveClass(entry.getValue().getAsString()));
+
+        }
+        relationTypes.put(type, props);
+
+      }
+
+      for (JsonElement item : relationsArray) {
+        JsonObject obj = item.getAsJsonObject();
+        // Parse the Source/Taget nodeTypes
+
+        String relationType = obj.get(SCHEMA_RELATIONSHIP_TYPE).getAsString();
+        String key = obj.get(SCHEMA_SOURCE_NODE_TYPE).getAsString() + ":"
+            + obj.get(SCHEMA_TARGET_NODE_TYPE).getAsString() + ":" + relationType;
+
+
+        if (!relationTypes.containsKey(relationType)) {
+          throw new CrudException(SCHEMA_RELATIONSHIP_TYPE + ": " + relationType + " not found",
+              Status.BAD_REQUEST);
+        }
+
+        relations.put(key, relationTypes.get(relationType));
+      }
+    } catch (Exception e) {
+      throw new CrudException(e.getMessage(), Status.BAD_REQUEST);
+    }
+
+  }
+
+
+  public HashMap<String, Class<?>> lookupRelation(String key) {
+    return this.relations.get(key);
+  }
+
+  public HashMap<String, Class<?>> lookupRelationType(String type) {
+    return this.relationTypes.get(type);
+  }
+
+  public boolean isValidType(String type) {
+    return relationTypes.containsKey(type);
+  }
+
+  private Class<?> resolveClass(String type) throws CrudException, ClassNotFoundException {
+    Class<?> clazz = Class.forName(type);
+    validateClassTypes(clazz);
+    return clazz;
+  }
+
+  private void validateClassTypes(Class<?> clazz) throws CrudException {
+    if (!clazz.isAssignableFrom(Integer.class) && !clazz.isAssignableFrom(Double.class)
+        && !clazz.isAssignableFrom(Boolean.class) && !clazz.isAssignableFrom(String.class)) {
+      throw new CrudException("", Status.BAD_REQUEST);
+    }
+  }
+
+
+}
diff --git a/src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java b/src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java
new file mode 100644 (file)
index 0000000..51b33d0
--- /dev/null
@@ -0,0 +1,158 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.schema;
+
+import org.apache.commons.io.IOUtils;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.logging.CrudServiceMsgs;
+import org.openecomp.crud.util.CrudServiceConstants;
+import org.openecomp.crud.util.FileWatcher;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.ws.rs.core.Response.Status;
+import javax.xml.bind.JAXBException;
+
+
+public class RelationshipSchemaLoader {
+
+  private static Map<String, RelationshipSchema> versionContextMap
+      = new ConcurrentHashMap<String, RelationshipSchema>();
+  private static SortedSet<Integer> versions = new TreeSet<Integer>();
+  private static Map<String, Timer> timers = new ConcurrentHashMap<String, Timer>();
+
+  final static Pattern filePattern = Pattern.compile("aai_relationship_(.*).json");
+
+
+  private static org.openecomp.cl.api.Logger logger = LoggerFactory.getInstance()
+      .getLogger(RelationshipSchemaLoader.class.getName());
+
+  public synchronized static void loadModels() {
+
+    File[] listOfFiles = new File(CrudServiceConstants.CRD_HOME_MODEL).listFiles();
+
+    if (listOfFiles != null) {
+      for (File file : listOfFiles) {
+        if (file.isFile()) {
+          Matcher matcher = filePattern.matcher(file.getName());
+          if (matcher.matches()) {
+            try {
+              RelationshipSchemaLoader.loadModel(matcher.group(1), file);
+            } catch (Exception e) {
+              logger.error(CrudServiceMsgs.INVALID_OXM_FILE, file.getName(), e.getMessage());
+            }
+          }
+
+        }
+      }
+    } else {
+      logger.error(CrudServiceMsgs.INVALID_OXM_DIR, CrudServiceConstants.CRD_HOME_MODEL);
+    }
+
+
+  }
+
+  private static void addtimer(String version, File file) {
+    TimerTask task = null;
+    task = new FileWatcher(
+        file) {
+      protected void onChange(File file) {
+        // here we implement the onChange
+        logger.info(CrudServiceMsgs.OXM_FILE_CHANGED, file.getName());
+
+        try {
+          RelationshipSchemaLoader.loadModel(version, file);
+        } catch (Exception e) {
+          e.printStackTrace();
+        }
+
+      }
+    };
+
+    if (!timers.containsKey(version)) {
+      Timer timer = new Timer("aai_relationship_" + version);
+      timer.schedule(task, new Date(), 10000);
+      timers.put(version, timer);
+
+    }
+  }
+
+  private synchronized static void loadModel(String version, File file)
+      throws JAXBException, IOException, CrudException {
+
+    InputStream inputStream = new FileInputStream(file);
+    String content = IOUtils.toString(inputStream, "UTF-8");
+    versionContextMap.put(version, new RelationshipSchema(content));
+    addtimer(version, file);
+    versions.add(Integer.parseInt(version.substring(1)));
+  }
+
+  public static RelationshipSchema getSchemaForVersion(String version) throws CrudException {
+    if (versionContextMap == null || versionContextMap.isEmpty()) {
+      loadModels();
+    } else if (!versionContextMap.containsKey(version)) {
+      try {
+        loadModel(version, new File(CrudServiceConstants.CRD_HOME_MODEL + "aai_relationship_"
+            + version + ".json"));
+      } catch (Exception e) {
+        throw new CrudException("", Status.NOT_FOUND);
+      }
+    }
+
+    return versionContextMap.get(version);
+  }
+
+  public static String getLatestSchemaVersion() throws CrudException {
+    return "v" + versions.last();
+  }
+
+  public static Map<String, RelationshipSchema> getVersionContextMap() {
+    return versionContextMap;
+  }
+
+  public static void setVersionContextMap(HashMap<String, RelationshipSchema> versionContextMap) {
+    RelationshipSchemaLoader.versionContextMap = versionContextMap;
+  }
+
+  public static void main(String[] args) throws FileNotFoundException, Exception {
+    File initialFile = new File("C:\\Software\\gizmo\\src\\main\\java\\org\\openecomp\\schema\\vio.json");
+
+    loadModel("v8", initialFile);
+  }
+
+}
diff --git a/src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java b/src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java
new file mode 100644 (file)
index 0000000..552b60a
--- /dev/null
@@ -0,0 +1,341 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.schema;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+
+import org.openecomp.crud.entity.Edge;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.service.EdgePayload;
+import org.openecomp.crud.util.CrudServiceUtil;
+import org.radeox.util.logging.Logger;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.ws.rs.core.Response.Status;
+
+public class RelationshipSchemaValidator {
+
+  public static final String SOURCE_NODE = "source";
+  public static final String TARGET_NODE = "target";
+
+  final static Pattern urlPattern = Pattern.compile("services/inventory/(.*)/(.*)/(.*)");
+
+  public static Map<String, Object> resolveCollectionfilter(String version, String type,
+                                                            Map<String, String> filter)
+      throws CrudException {
+
+    RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+    if (schema == null) {
+      throw new CrudException("", Status.NOT_FOUND);
+    }
+
+    HashMap<String, Class<?>> props = schema.lookupRelationType(type);
+    Map<String, Object> result = new HashMap<String, Object>();
+
+    for (String key : filter.keySet()) {
+
+      if (props.containsKey(key)) {
+        try {
+          Object value = CrudServiceUtil.validateFieldType(filter.get(key), props.get(key));
+          result.put(key, value);
+        } catch (Exception ex) {
+          // Skip any exceptions thrown while validating the filter
+          // key value
+          continue;
+        }
+      }
+    }
+
+    return result;
+
+  }
+
+  public static void validateType(String version, String type) throws CrudException {
+
+    RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+    if (!schema.isValidType(type)) {
+      throw new CrudException("Invalid " + RelationshipSchema.SCHEMA_RELATIONSHIP_TYPE
+          + ": " + type,
+          Status.BAD_REQUEST);
+    }
+
+  }
+
+  public static Edge validateIncomingAddPayload(String version, String type, Vertex sourceNode,
+                                                Vertex targetNode, JsonElement properties)
+      throws CrudException {
+    EdgePayload payload = new EdgePayload();
+    payload.setSource("services/inventory/" + version + "/" + sourceNode.getType()
+        + "/" + sourceNode.getId().get());
+    payload.setTarget("services/inventory/" + version + "/" + targetNode.getType()
+        + "/" + targetNode.getId().get());
+    payload.setType(type);
+    payload.setProperties(properties);
+    return validateIncomingAddPayload(version, type, payload);
+  }
+
+  public static Edge validateIncomingAddPayload(String version, String type, EdgePayload payload)
+      throws CrudException {
+    RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+
+    try {
+
+      if (payload.getSource() == null || payload.getTarget() == null) {
+        throw new CrudException("Source/Target not specified", Status.BAD_REQUEST);
+      }
+
+      Matcher sourceMatcher = urlPattern.matcher(payload.getSource());
+      Matcher targetMatcher = urlPattern.matcher(payload.getTarget());
+
+      if (!sourceMatcher.matches() || !targetMatcher.matches()) {
+        throw new CrudException("Invalid Source/Target Urls", Status.BAD_REQUEST);
+      }
+
+      // create key based on source:target:relationshipType
+      String sourceNodeType = sourceMatcher.group(2);
+      String targetNodeType = targetMatcher.group(2);
+
+      String sourceNodeId = sourceMatcher.group(3);
+      String targetNodeId = targetMatcher.group(3);
+
+      String key = sourceNodeType + ":" + targetNodeType + ":" + type;
+
+      // find the validate the key from the schema
+      HashMap<String, Class<?>> schemaObject = schema.lookupRelation(key);
+
+      if (schemaObject == null) {
+        throw new CrudException("Invalid source/target/relationship type: " + key,
+            Status.BAD_REQUEST);
+      }
+
+      Edge.Builder modelEdgeBuilder = new Edge.Builder(type);
+
+      modelEdgeBuilder.source(new Vertex.Builder(sourceNodeType).id(sourceNodeId).build());
+      modelEdgeBuilder.target(new Vertex.Builder(targetNodeType).id(targetNodeId).build());
+
+      // validate it properties
+      validateEdgeProps(modelEdgeBuilder, payload.getProperties(), schemaObject);
+
+      return modelEdgeBuilder.build();
+    } catch (Exception ex) {
+
+      throw new CrudException(ex.getMessage(), Status.BAD_REQUEST);
+    }
+
+  }
+
+  public static Edge validateIncomingPatchPayload(Edge edge, String version, EdgePayload payload)
+      throws CrudException {
+    RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+
+    try {
+      if (payload.getSource() != null) {
+        Matcher sourceMatcher = urlPattern.matcher(payload.getSource());
+
+        if (!sourceMatcher.matches()) {
+          throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST);
+        }
+        String sourceNodeId = sourceMatcher.group(3);
+        if (!sourceNodeId.equals(edge.getSource().getId().get())) {
+          throw new CrudException("Source can't be updated", Status.BAD_REQUEST);
+        }
+      }
+
+      if (payload.getTarget() != null) {
+        Matcher targetMatcher = urlPattern.matcher(payload.getTarget());
+
+        if (!targetMatcher.matches()) {
+          throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST);
+        }
+        String sourceNodeId = targetMatcher.group(3);
+        if (!sourceNodeId.equals(edge.getTarget().getId().get())) {
+          throw new CrudException("Target can't be updated", Status.BAD_REQUEST);
+        }
+      }
+      // create key based on source:target:relationshipType
+
+      String key = edge.getSource().getType() + ":" + edge.getTarget().getType()
+          + ":" + edge.getType();
+
+      // find the validate the key from the schema
+      HashMap<String, Class<?>> schemaObject = schema.lookupRelation(key);
+
+      if (schemaObject == null) {
+        Logger.warn("key :" + key
+            + " not found in relationship schema . Skipping the schema validation");
+        return edge;
+      }
+
+      Set<Map.Entry<String, JsonElement>> entries = payload.getProperties()
+          .getAsJsonObject().entrySet();
+
+      for (Map.Entry<String, JsonElement> entry : entries) {
+
+        if (!schemaObject.containsKey(entry.getKey())) {
+          throw new CrudException("Invalid property: " + entry.getKey(), Status.BAD_REQUEST);
+        } else if (entry.getValue() instanceof JsonNull && edge.getProperties()
+            .containsKey(entry.getKey())) {
+          edge.getProperties().remove(entry.getKey());
+        } else if (!(entry.getValue() instanceof JsonNull)) {
+          Object value = CrudServiceUtil.validateFieldType(entry.getValue().getAsString(),
+              schemaObject.get(entry.getKey()));
+          edge.getProperties().put(entry.getKey(), value);
+        }
+
+      }
+
+      return edge;
+
+    } catch (Exception ex) {
+
+      throw new CrudException(ex.getMessage(), Status.BAD_REQUEST);
+    }
+  }
+
+  public static Edge validateIncomingUpdatePayload(Edge edge, String version, Vertex sourceNode,
+                                                   Vertex targetNode, JsonElement properties)
+      throws CrudException {
+    EdgePayload payload = new EdgePayload();
+    payload.setSource("services/inventory/" + version + "/" + sourceNode.getType()
+        + "/" + sourceNode.getId().get());
+    payload.setTarget("services/inventory/" + version + "/" + targetNode.getType()
+        + "/" + targetNode.getId().get());
+    payload.setType(edge.getType());
+    payload.setProperties(properties);
+    return validateIncomingUpdatePayload(edge, version, payload);
+  }
+
+  public static Edge validateIncomingUpdatePayload(Edge edge, String version, EdgePayload payload)
+      throws CrudException {
+    RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+
+    try {
+
+      if (payload.getSource() != null) {
+        Matcher sourceMatcher = urlPattern.matcher(payload.getSource());
+
+        if (!sourceMatcher.matches()) {
+          throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST);
+        }
+        String sourceNodeId = sourceMatcher.group(3);
+        if (!sourceNodeId.equals(edge.getSource().getId().get())) {
+          throw new CrudException("Source can't be updated", Status.BAD_REQUEST);
+        }
+      }
+
+      if (payload.getTarget() != null) {
+        Matcher targetMatcher = urlPattern.matcher(payload.getTarget());
+
+        if (!targetMatcher.matches()) {
+          throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST);
+        }
+        String sourceNodeId = targetMatcher.group(3);
+        if (!sourceNodeId.equals(edge.getTarget().getId().get())) {
+          throw new CrudException("Target can't be updated", Status.BAD_REQUEST);
+        }
+      }
+      // create key based on source:target:relationshipType
+
+      String key = edge.getSource().getType() + ":" + edge.getTarget().getType()
+          + ":" + edge.getType();
+
+      // find the validate the key from the schema
+      HashMap<String, Class<?>> schemaObject = schema.lookupRelation(key);
+
+      if (schemaObject == null) {
+        Logger.warn("key :" + key
+            + " not found in relationship schema . Skipping the schema validation");
+        return edge;
+      }
+
+      Edge.Builder updatedEdgeBuilder = new Edge.Builder(edge.getType()).id(edge.getId().get());
+
+      updatedEdgeBuilder
+          .source(new Vertex.Builder(edge.getSource().getType()).id(edge.getSource().getId()
+              .get()).build());
+      updatedEdgeBuilder
+          .target(new Vertex.Builder(edge.getTarget().getType()).id(edge.getTarget().getId()
+              .get()).build());
+
+      validateEdgeProps(updatedEdgeBuilder, payload.getProperties(), schemaObject);
+
+      return updatedEdgeBuilder.build();
+    } catch (Exception ex) {
+
+      throw new CrudException(ex.getMessage(), Status.BAD_REQUEST);
+    }
+  }
+
+
+  private static void validateEdgeProps(Edge.Builder builder, JsonElement props,
+                                        HashMap<String, Class<?>> schemaObject)
+      throws CrudException {
+    Set<Map.Entry<String, JsonElement>> entries = props.getAsJsonObject().entrySet();
+
+    for (Map.Entry<String, JsonElement> entry : entries) {
+
+      if (!schemaObject.containsKey(entry.getKey())) {
+        throw new CrudException("Invalid property: " + entry.getKey(), Status.BAD_REQUEST);
+      } else {
+        Object value = CrudServiceUtil.validateFieldType(entry.getValue().getAsString(),
+            schemaObject.get(entry.getKey()));
+        builder.property(entry.getKey(), value);
+      }
+
+    }
+
+  }
+
+  public static Edge validateOutgoingPayload(String version, Edge edge) throws CrudException {
+
+    Edge.Builder modelEdgeBuilder = new Edge.Builder(edge.getType()).id(edge.getId()
+        .get()).source(edge.getSource())
+        .target(edge.getTarget());
+
+    RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+
+    String key = edge.getSource().getType() + ":" + edge.getTarget().getType()
+        + ":" + edge.getType();
+    HashMap<String, Class<?>> schemaObject = schema.lookupRelation(key);
+
+    if (schemaObject == null || schemaObject.isEmpty()) {
+      return edge;
+    }
+
+    for (String prop : edge.getProperties().keySet()) {
+      if (schemaObject.containsKey(prop)) {
+        modelEdgeBuilder.property(prop, edge.getProperties().get(prop));
+      }
+
+    }
+    return modelEdgeBuilder.build();
+  }
+
+}
diff --git a/src/main/resources/logging/CrudServiceMsgs.properties b/src/main/resources/logging/CrudServiceMsgs.properties
new file mode 100644 (file)
index 0000000..850574f
--- /dev/null
@@ -0,0 +1,75 @@
+#Resource key=Error Code|Message text|Resolution text |Description text
+#######
+#Newlines can be utilized to add some clarity ensuring continuing line
+#has atleast one leading space
+#ResourceKey=\
+#             CA0000E\
+#             Sample error msg txt\
+#             Sample resolution msg\
+#             Sample description txt
+#
+######
+#Error code classification category
+#000     Info/Debug
+#100    Permission errors
+#200    Availability errors/Timeouts
+#300    Data errors
+#400    Schema Interface type/validation errors
+#500    Business process errors
+#900    Unknown errors
+#
+########################################################################
+
+PROCESS_REST_REQUEST=\
+            CRD0001I|\
+            Received request {0} {1} from {2}.  Sending response: {3}|\
+            None.  Received the specified REST request from the source specified.|\
+            Received the specified REST request from the source specified, and the CRUD service sent the specified response.    
+
+INVALID_OXM_FILE=\
+            CRD0002I|\
+            Unable to parse schema file: {0} due to error : {1}\
+            
+OXM_FILE_CHANGED=\
+            CRD0003I|\
+            OXM file change detected: {0}\
+            
+STOPPING_CHAMP_DAO=\
+            CRD0004I|\
+            Stopping ChampDAO...
+
+INVALID_OXM_DIR=\
+            CRD0005I|\
+            Invalid OXM dir: {0}\
+      
+INSTANTIATE_GRAPH_DAO=\
+            CRD0006I|\
+            Instantiate data access layer for graph data store type: {0} graph: {1} using hosts: {2}
+
+LOADED_OXM_FILE=\
+            CRD0007I|\
+            Successfully loaded schema: {0}
+
+INVALID_GRAPH_BACKEND=\
+            CRD0301E|\
+            Unsupported graph database {0} specified.
+
+INSTANTIATE_GRAPH_BACKEND_ERR=\
+            CRD0302E|\
+            Failure instantiating {0} graph database backend.  Cause: {1}           
+
+EXCEPTION_DURING_METHOD_CALL=\
+            CRD0502E|\
+            Failed to {0} request for {1} due to: {2}|\
+            Check error cause|\
+            Method failed to execute
+
+OXM_LOAD_ERROR=\
+            CRD0503E|\
+            Unable to load OXM schema: {0}
+
+
+TITAN_GRAPH_INFO=\
+            CRD0504I|\
+            Titan Graph Info: {0}
+                   
\ No newline at end of file
diff --git a/src/main/runtime/context/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.context b/src/main/runtime/context/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.context
new file mode 100644 (file)
index 0000000..8514196
--- /dev/null
@@ -0,0 +1 @@
+{"context":{"contextClass":"ajsc.Context","contextId":"__module_ajsc_namespace_name__:__module_ajsc_namespace_version__","contextName":"__module_ajsc_namespace_name__","contextVersion":"__module_ajsc_namespace_version__","description":"__module_ajsc_namespace_name__ Context"}}
\ No newline at end of file
diff --git a/src/main/runtime/context/default#0.context b/src/main/runtime/context/default#0.context
new file mode 100644 (file)
index 0000000..d1b5ab4
--- /dev/null
@@ -0,0 +1 @@
+{"context":{"contextClass":"ajsc.Context","contextId":"default:0","contextName":"default","contextVersion":"0","description":"Default Context"}}
\ No newline at end of file
diff --git a/src/main/runtime/deploymentPackage/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.json b/src/main/runtime/deploymentPackage/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.json
new file mode 100644 (file)
index 0000000..d0954cf
--- /dev/null
@@ -0,0 +1 @@
+{"deploymentPackage":{"Class":"ajsc.DeploymentPackage","Id":"__module.ajsc.namespace.name__:__module_ajsc_namespace_version__","namespace":"__module_ajsc_namespace_name__","namespaceVersion":"__module_ajsc_namespace_version__","description":"__module_ajsc_namespace_name__ __module_ajsc_namespace_version__ - default description","userId":"ajsc"}}
\ No newline at end of file
diff --git a/src/main/runtime/shiroRole/ajscadmin.json b/src/main/runtime/shiroRole/ajscadmin.json
new file mode 100644 (file)
index 0000000..f5e981e
--- /dev/null
@@ -0,0 +1 @@
+{"shiroRoleClass":"ajsc.auth.ShiroRole","shiroRoleId":"ajscadmin","name":"ajscadmin","permissions":"[ajscadmin:*, ajsc:*]"}
\ No newline at end of file
diff --git a/src/main/runtime/shiroRole/contextadmin#__module.ajsc.namespace.name__.json b/src/main/runtime/shiroRole/contextadmin#__module.ajsc.namespace.name__.json
new file mode 100644 (file)
index 0000000..2dae9f5
--- /dev/null
@@ -0,0 +1 @@
+{"shiroRoleClass":"ajsc.auth.ShiroRole","shiroRoleId":"contextadmin:__module_ajsc_namespace_name__","name":"contextadmin:__module_ajsc_namespace_name__","permissions":"[]"}
\ No newline at end of file
diff --git a/src/main/runtime/shiroRole/contextadmin#default.json b/src/main/runtime/shiroRole/contextadmin#default.json
new file mode 100644 (file)
index 0000000..5de814e
--- /dev/null
@@ -0,0 +1 @@
+{"shiroRoleClass":"ajsc.auth.ShiroRole","shiroRoleId":"contextadmin:default","name":"contextadmin:default","permissions":"[]"}
\ No newline at end of file
diff --git a/src/main/runtime/shiroUser/ajsc.json b/src/main/runtime/shiroUser/ajsc.json
new file mode 100644 (file)
index 0000000..f4c7855
--- /dev/null
@@ -0,0 +1 @@
+{"shiroUserClass":"ajsc.auth.ShiroUser","shiroUserId":"ajsc","passwordHash":"9471697417008c880720ba54c6038791ad7e98f3b88136fe34f4d31a462dd27a","permissions":"[*:*]","username":"ajsc"}
\ No newline at end of file
diff --git a/src/main/runtime/shiroUserRole/ajsc#ajscadmin.json b/src/main/runtime/shiroUserRole/ajsc#ajscadmin.json
new file mode 100644 (file)
index 0000000..cb8d483
--- /dev/null
@@ -0,0 +1 @@
+{"shiroUserRoleClass":"ajsc.auth.ShiroUserRole","shiroUserRoleId":"ajsc:ajscadmin","roleId":"ajscadmin","userId":"ajsc"}
\ No newline at end of file
diff --git a/src/main/runtime/shiroUserRole/ajsc#contextadmin#__module.ajsc.namespace.name__.json b/src/main/runtime/shiroUserRole/ajsc#contextadmin#__module.ajsc.namespace.name__.json
new file mode 100644 (file)
index 0000000..95d2361
--- /dev/null
@@ -0,0 +1 @@
+{"shiroUserRoleClass":"ajsc.auth.ShiroUserRole","shiroUserRoleId":"ajsc:contextadmin:__module_ajsc_namespace_name__","roleId":"contextadmin:__module_ajsc_namespace_name__","userId":"ajsc"}
\ No newline at end of file
diff --git a/src/main/runtime/shiroUserRole/ajsc#contextadmin#default.json b/src/main/runtime/shiroUserRole/ajsc#contextadmin#default.json
new file mode 100644 (file)
index 0000000..2bd5063
--- /dev/null
@@ -0,0 +1 @@
+{"shiroUserRoleClass":"ajsc.auth.ShiroUserRole","shiroUserRoleId":"ajsc:contextadmin:default","roleId":"contextadmin:default","userId":"ajsc"}
\ No newline at end of file
diff --git a/src/test/java/org/openecomp/crud/dao/champ/ChampDaoTest.java b/src/test/java/org/openecomp/crud/dao/champ/ChampDaoTest.java
new file mode 100644 (file)
index 0000000..b11e274
--- /dev/null
@@ -0,0 +1,624 @@
+package org.openecomp.crud.dao.champ;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.openecomp.crud.dao.GraphDao;
+import org.openecomp.crud.entity.Edge;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * This suite of tests validates the basic functionality of the {@link ChampDao}.
+ */
+public class ChampDaoTest {
+
+  private static final String GRAPH_NAME = "my_test_graph";
+
+  private GraphDao champDao = null;
+
+
+  /**
+   * Perform setup steps that must be done prior to executing each test.
+   */
+  @Before
+  public void setup() {
+
+    // Create an instance of the Champ DAO, backed by the Champ library's in-memory back end
+    // for testing purposes.
+    Properties champDaoProperties = new Properties();
+    champDaoProperties.put(ChampDao.CONFIG_STORAGE_BACKEND, "in-memory");
+    champDaoProperties.put(ChampDao.CONFIG_GRAPH_NAME, GRAPH_NAME);
+    champDao = new ChampDao(champDaoProperties);
+  }
+
+
+  /**
+   * Perform tear down steps that must be done after executing each test.
+   */
+  @After
+  public void tearDown() {
+
+    // Release the Champ DAO instance that we were using for the test.
+    if (champDao != null) {
+      ((ChampDao) champDao).close();
+    }
+  }
+
+
+  /**
+   * Tests the ability of the {@link ChampDao} to create a vertex.
+   *
+   * @throws CrudException
+   */
+  @Test
+  public void createVertexTest() throws CrudException {
+
+    String VERTEX_TYPE = "Test_Vertex";
+
+    Map<String, Object> properties = new HashMap<String, Object>();
+    properties.put("property1", "something");
+    properties.put("property2", "something else");
+
+    // Create the vertex.
+    Vertex createdVertex = champDao.addVertex(VERTEX_TYPE, properties);
+
+    // Validate that the returned {@link Vertex} has the right label assigned to it.
+    assertTrue("Unexpected vertex type '" + createdVertex.getType() + "' returned from DAO",
+        createdVertex.getType().equals(VERTEX_TYPE));
+
+    // Validate that all of the properties that we provided to the DAO are in fact assigned
+    // to the {@link Vertex} that we got back.
+    assertTrue("Vertex property list returned from DAO did not contain all expected properties - expected: " +
+            properties.keySet() + " actual: " + createdVertex.getProperties().keySet(),
+        createdVertex.getProperties().keySet().containsAll(properties.keySet()));
+
+    // Validate that the values assigned to the properties in the returned {@link Vertex}
+    // match the ones that we provided.
+    for (String propertyKey : properties.keySet()) {
+
+      assertTrue(createdVertex.getProperties().get(propertyKey).equals(properties.get(propertyKey)));
+    }
+  }
+
+
+  /**
+   * Tests the ability of the {@link ChampDao} to retrieve a vertex from the graph data store
+   * by its unique identifier.
+   *
+   * @throws CrudException
+   */
+  @Test
+  public void getVertexByIdTest() throws CrudException {
+
+    String VERTEX_TYPE = "Test_Vertex";
+
+    Map<String, Object> properties = new HashMap<String, Object>();
+    properties.put("property1", "something");
+    properties.put("property2", "something else");
+
+    // Create the vertex.
+    Vertex createdVertex = champDao.addVertex(VERTEX_TYPE, properties);
+
+    // Make sure the {@link Vertex} returned from the create method includes an id that we can
+    // use to retrieve it.
+    assertTrue("No valid id returned for the created vertex", createdVertex.getId().isPresent());
+
+    // Now, retrieve the {@link Vertex} by its identifier.
+    Vertex retrievedVertex = champDao.getVertex(createdVertex.getId().get(), VERTEX_TYPE);
+
+    // Validate that the retrieved {@link Vertex} has the right label assigned to it.
+    assertTrue("Unexpected vertex type '" + retrievedVertex.getType() + "' returned from DAO",
+        retrievedVertex.getType().equals(VERTEX_TYPE));
+
+    // Validate that all of the properties that we provided when we created the {@link Vertex}
+    // are present in the {@link Vertex} that we retrieved.
+    assertTrue("Vertex property list returned from DAO did not contain all expected properties - expected: " +
+            properties.keySet() + " actual: " + retrievedVertex.getProperties().keySet(),
+        retrievedVertex.getProperties().keySet().containsAll(properties.keySet()));
+
+    // Validate that the values assigned to the properties in the retrieved {@link Vertex}
+    // match the ones that we provided when we created it.
+    for (String propertyKey : properties.keySet()) {
+
+      assertTrue(retrievedVertex.getProperties().get(propertyKey).equals(properties.get(propertyKey)));
+    }
+  }
+
+
+  /**
+   * Tests the ability of the {@link ChampDao} to update an already existing vertex.
+   *
+   * @throws CrudException
+   */
+  @Test
+  public void updateVertexTest() throws CrudException {
+
+    final String VERTEX_TYPE = "Test_Vertex";
+
+    Map<String, Object> properties = new HashMap<String, Object>();
+    properties.put("property1", "something");
+    properties.put("property2", "something else");
+
+    // Create the vertex.
+    Vertex createdVertex = champDao.addVertex(VERTEX_TYPE, properties);
+
+    // Make sure the {@link Vertex} returned from the create method includes an id that we can
+    // use to retrieve it.
+    assertTrue("No valid id returned for the created vertex", createdVertex.getId().isPresent());
+
+    // Modify the properties list...
+    properties.put("property3", "a new property");
+    properties.remove("property1");
+
+    // ...and apply it to our vertex.
+    Vertex updatedVertex = champDao.updateVertex(createdVertex.getId().get(), createdVertex.getType(), properties);
+
+    assertTrue("Vertex property list returned from DAO update operation did not contain all expected properties - expected: " +
+            properties.keySet() + " actual: " + updatedVertex.getProperties().keySet(),
+        updatedVertex.getProperties().keySet().containsAll(properties.keySet()));
+
+    // Validate that the values assigned to the properties in the updated {@link Vertex}
+    // match the ones that we provided when we created it.
+    for (String propertyKey : properties.keySet()) {
+
+      assertTrue("Unexpected value for property '" + propertyKey + "' - Expected: " +
+              properties.get(propertyKey) + "  Actual: " +
+              updatedVertex.getProperties().get(propertyKey),
+          updatedVertex.getProperties().get(propertyKey).equals(properties.get(propertyKey)));
+    }
+
+    // Validate that the property that we removed is NOT in the set of properties from our
+    // updated {@link Vertex}.
+    assertFalse("Property 'property1' should no longer be associated with updated vertex",
+        updatedVertex.getProperties().containsKey("property1"));
+  }
+
+
+  /**
+   * Tests the ability of the {@link ChampDao} to retrieve multiple vertices which match
+   * a particular set of supplied properties.
+   *
+   * @throws CrudException
+   */
+  @Test
+  public void getVerticesTest() throws CrudException {
+
+    final String FIRST_VERTEX_TYPE = "pserver";
+    final String SECOND_VERTEX_TYPE = "complex";
+
+    // Create some vertices.
+
+    Map<String, Object> vertex1Properties = new HashMap<String, Object>();
+    vertex1Properties.put("O/S", "Linux");
+    vertex1Properties.put("version", "6.5");
+    vertex1Properties.put("hostname", "kll0001");
+    champDao.addVertex(FIRST_VERTEX_TYPE, vertex1Properties);
+
+    Map<String, Object> vertex2Properties = new HashMap<String, Object>();
+    vertex2Properties.put("O/S", "Linux");
+    vertex2Properties.put("version", "6.5");
+    vertex2Properties.put("hostname", "kll0002");
+    champDao.addVertex(FIRST_VERTEX_TYPE, vertex2Properties);
+
+    Map<String, Object> vertex3Properties = new HashMap<String, Object>();
+    vertex3Properties.put("O/S", "Linux");
+    vertex3Properties.put("version", "7.2");
+    vertex3Properties.put("hostname", "kll0003");
+    champDao.addVertex(FIRST_VERTEX_TYPE, vertex3Properties);
+
+    Map<String, Object> vertex4Properties = new HashMap<String, Object>();
+    vertex4Properties.put("O/S", "Windows");
+    vertex4Properties.put("version", "10");
+    vertex4Properties.put("hostname", "Dev Laptop");
+    champDao.addVertex(FIRST_VERTEX_TYPE, vertex4Properties);
+
+    Map<String, Object> vertex5Properties = new HashMap<String, Object>();
+    vertex5Properties.put("Street", "Baker");
+    vertex5Properties.put("Number", "222B");
+    champDao.addVertex(SECOND_VERTEX_TYPE, vertex5Properties);
+
+    // Create a set of properties to use for our query.
+    Map<String, Object> queryProperties = new HashMap<String, Object>();
+    queryProperties.put("O/S", "Linux");
+    queryProperties.put("version", "6.5");
+
+    // Validate that we filter our 'get vertices' results by type
+    List<Vertex> allVerticesByType = champDao.getVertices(FIRST_VERTEX_TYPE, MapBuilder.builder().build());
+    for (Vertex v : allVerticesByType) {
+      assertTrue("Unexpected vertex type returned from query.  Expected: " +
+              FIRST_VERTEX_TYPE + " Actual: " + v.getType(),
+          v.getType().equals(FIRST_VERTEX_TYPE));
+    }
+
+    // Now, request the vertices that match our parameters.
+    List<Vertex> vertices = champDao.getVertices(FIRST_VERTEX_TYPE, queryProperties);
+
+    // Validate that got back the expected number of vertices.
+    assertEquals(vertices.size(), 2);
+
+    // Validate that the vertices we got back contain the expected parameters.
+    for (Vertex v : vertices) {
+
+      assertTrue("Vertex from query result does not contain expected vertex 'O/S'",
+          v.getProperties().containsKey("O/S"));
+      assertTrue("Vertex from query result contains unexpected value for 'O/S' parameter - Expected: 'Linux'  Actual: '" +
+              v.getProperties().get("O/S") + "'",
+          v.getProperties().get("O/S").equals("Linux"));
+
+      assertTrue("Vertex from query result does not contain expected vertex 'O/S'",
+          v.getProperties().containsKey("version"));
+      assertTrue("Vertex from query result contains unexpected value for 'O/S' parameter - Expected: 'Linux'  Actual: '" +
+              v.getProperties().get("O/S") + "'",
+          v.getProperties().get("version").equals("6.5"));
+    }
+  }
+
+  @Test
+  public void deleteVertexTest() throws CrudException {
+
+    boolean deletedVertexNotFound = false;
+
+    // Create a vertex.
+    Vertex createdVertex = champDao.addVertex("test_type", MapBuilder.builder()
+        .withKeyValue("O/S", "Linux")
+        .withKeyValue("version", "6.5")
+        .withKeyValue("hostname", "kll0001")
+        .build());
+
+    // Verify that we can retrieve the vertex from the graph data base.
+    Vertex retrievedVertex = champDao.getVertex(createdVertex.getId().get(), "test_type");
+
+    // Now, delete the vertex.
+    champDao.deleteVertex(createdVertex.getId().get(), "test_type");
+
+    // Now, try to retrieve it again.  This time we should fail to find it.
+    try {
+      champDao.getVertex(createdVertex.getId().get(), "test_type");
+
+    } catch (CrudException e) {
+      assertTrue(e.getMessage().contains("No vertex with id"));
+      deletedVertexNotFound = true;
+    }
+
+    assertTrue("Should not have been able to retrieve deleted vertex", deletedVertexNotFound);
+  }
+
+  @Test
+  public void createEdgeTest() throws CrudException {
+
+    String EDGE_TYPE = "has";
+
+    // Create the source vertex for the edge.
+    Map<String, Object> srcVertexProperties = new HashMap<String, Object>();
+    srcVertexProperties.put("O/S", "Linux");
+    srcVertexProperties.put("version", "6.5");
+    srcVertexProperties.put("hostname", "kll0001");
+    Vertex sourceVertex = champDao.addVertex("vserver", srcVertexProperties);
+
+    // Create the target vertex for the edge.
+    Map<String, Object> dstVertexProperties = new HashMap<String, Object>();
+    dstVertexProperties.put("O/S", "Linux");
+    dstVertexProperties.put("version", "6.5");
+    dstVertexProperties.put("hostname", "kll0002");
+    Vertex destVertex = champDao.addVertex("VNF", dstVertexProperties);
+
+    // Now, create the edge itself.
+    Map<String, Object> edgeProperties = new HashMap<String, Object>();
+    edgeProperties.put("prop", "val");
+    Edge createdEdge = champDao.addEdge("has", sourceVertex, destVertex, edgeProperties);
+
+    // Validate that the Edge object returned from the create method matches what we were
+    // trying to create.
+    assertTrue("Unexpected type for Edge returned from create method.  Expected: " + EDGE_TYPE
+            + " Actual: " + createdEdge.getType(),
+        createdEdge.getType().equals("has"));
+    assertTrue("Unexpected properties for Edge returned from create method.  Expected: " + edgeProperties
+            + " Actual: " + createdEdge.getProperties(),
+        createdEdge.getProperties().equals(edgeProperties));
+
+  }
+
+  @Test
+  public void createEdgeWithMissingSrcOrTargetTest() throws CrudException {
+
+    String EDGE_TYPE = "has";
+
+    // Create the source vertex for the edge.
+    Map<String, Object> srcVertexProperties = new HashMap<String, Object>();
+    srcVertexProperties.put("O/S", "Linux");
+    srcVertexProperties.put("version", "6.5");
+    srcVertexProperties.put("hostname", "kll0001");
+    Vertex sourceVertex = champDao.addVertex("vserver", srcVertexProperties);
+
+    // Create the target vertex for the edge.
+    Map<String, Object> dstVertexProperties = new HashMap<String, Object>();
+    dstVertexProperties.put("O/S", "Linux");
+    dstVertexProperties.put("version", "6.5");
+    dstVertexProperties.put("hostname", "kll0002");
+    Vertex destVertex = champDao.addVertex("VNF", dstVertexProperties);
+
+    // Now, try creating the Edge but specify an id for the source vertex that does
+    // not exist.
+    Map<String, Object> edgeProperties = new HashMap<String, Object>();
+    edgeProperties.put("prop", "val");
+    try {
+      champDao.addEdge(EDGE_TYPE, new Vertex.Builder("miss").id("99").build(), destVertex, edgeProperties);
+    } catch (CrudException e) {
+      assertTrue(e.getMessage().contains("Error creating edge - source vertex"));
+    }
+
+    // Now, try created the Edge with a valid source vertex, but specify an id for the
+    // target vertex that does not exist.
+    try {
+      champDao.addEdge(EDGE_TYPE, sourceVertex, new Vertex.Builder("miss").id("99").build(), edgeProperties);
+    } catch (CrudException e) {
+      assertTrue(e.getMessage().contains("Error creating edge - target vertex"));
+    }
+
+  }
+
+  @Test
+  public void getEdgeByIdTest() throws CrudException {
+
+    String EDGE_TYPE = "has";
+
+    // Create the source vertex for the edge.
+    Map<String, Object> srcVertexProperties = new HashMap<String, Object>();
+    srcVertexProperties.put("O/S", "Linux");
+    srcVertexProperties.put("version", "6.5");
+    srcVertexProperties.put("hostname", "kll0001");
+    Vertex sourceVertex = champDao.addVertex("vserver", srcVertexProperties);
+
+    // Create the target vertex for the edge.
+    Map<String, Object> dstVertexProperties = new HashMap<String, Object>();
+    dstVertexProperties.put("O/S", "Linux");
+    dstVertexProperties.put("version", "6.5");
+    dstVertexProperties.put("hostname", "kll0002");
+    Vertex destVertex = champDao.addVertex("VNF", dstVertexProperties);
+
+    // Now, create the edge itself.
+    Map<String, Object> edgeProperties = new HashMap<String, Object>();
+    edgeProperties.put("prop", "val");
+    Edge createdEdge = champDao.addEdge("has", sourceVertex, destVertex, edgeProperties);
+
+    // Retrieve the edge we just created by specifying its unique identifier.
+    Edge retrievedEdge = champDao.getEdge(createdEdge.getId().get(), "has");
+
+    // Validate that the contents of the object that we got back matches what we thought we
+    // created.
+    assertTrue("Unexpected type for Edge returned from get method.  Expected: " + EDGE_TYPE
+            + " Actual: " + retrievedEdge.getType(),
+        retrievedEdge.getType().equals(EDGE_TYPE));
+    assertTrue("Unexpected properties for Edge returned from get method.  Expected: " + edgeProperties
+            + " Actual: " + retrievedEdge.getProperties(),
+        retrievedEdge.getProperties().equals(edgeProperties));
+  }
+
+  @Test
+  public void getEdgesTest() throws CrudException {
+
+    final String EDGE_TYPE_HAS = "has";
+    final String EDGE_TYPE_RUNS = "runs";
+
+    // Create some vertices and edges that we can query agains.
+    Vertex complex = champDao.addVertex("complex", MapBuilder.builder()
+        .withKeyValue("Province", "Ontario")
+        .withKeyValue("City", "Ottawa")
+        .withKeyValue("Street", "303 Terry Fox")
+        .build());
+
+    Vertex vserver = champDao.addVertex("vserver", MapBuilder.builder()
+        .withKeyValue("O/S", "Linux")
+        .withKeyValue("version", "6.5")
+        .withKeyValue("hostname", "kll0001")
+        .build());
+
+    Vertex vnf1 = champDao.addVertex("vserver", MapBuilder.builder()
+        .withKeyValue("Application", "OpenDaylight")
+        .build());
+
+    Vertex vnf2 = champDao.addVertex("vserver", MapBuilder.builder()
+        .withKeyValue("Application", "Cammunda")
+        .build());
+
+    Edge edge1 = champDao.addEdge(EDGE_TYPE_HAS, complex, vserver,
+        MapBuilder.builder()
+            .withKeyValue("usesResource", "false")
+            .withKeyValue("hasDelTarget", "false")
+            .build());
+
+    Edge edge2 = champDao.addEdge(EDGE_TYPE_RUNS, vserver, vnf1,
+        MapBuilder.builder()
+            .withKeyValue("usesResource", "false")
+            .withKeyValue("hasDelTarget", "true")
+            .build());
+
+    Edge edge3 = champDao.addEdge(EDGE_TYPE_RUNS, vserver, vnf2,
+        MapBuilder.builder()
+            .withKeyValue("usesResource", "false")
+            .withKeyValue("hasDelTarget", "false")
+            .build());
+
+    // Query for all HAS edges.
+    List<Edge> hasEdges = champDao.getEdges(EDGE_TYPE_HAS, new HashMap<String, Object>());
+
+    assertEquals("Unexpected number of edges of type 'has' found.  Expected: 1 Actual: " + hasEdges.size(),
+        hasEdges.size(), 1);
+    assertTrue("Result of query for 'has' type edges does not contain the expected results",
+        containsEdge(edge1, hasEdges));
+
+    // Query for all RUNS edges.
+    List<Edge> runsEdges = champDao.getEdges(EDGE_TYPE_RUNS, new HashMap<String, Object>());
+
+    assertEquals("Unexpected number of edges of type 'runs' found.  Expected: 2 Actual: " + runsEdges.size(),
+        runsEdges.size(), 2);
+    assertTrue("Result of query for 'runs' type edges does not contain the expected results",
+        containsEdge(edge2, runsEdges));
+    assertTrue("Result of query for 'runs' type edges does not contain the expected results",
+        containsEdge(edge2, runsEdges));
+
+    // Query for all HAS edges with the property 'hasDelTarget' equal to 'true'.
+    List<Edge> runsEdgesWithDelTargetTrue =
+        champDao.getEdges(EDGE_TYPE_RUNS, MapBuilder.builder()
+            .withKeyValue("hasDelTarget", "true")
+            .build());
+
+    assertEquals("Unexpected number of edges of type 'has' with 'hasDelTarget=true' found.  Expected: 1 Actual: "
+            + runsEdgesWithDelTargetTrue.size(),
+        runsEdgesWithDelTargetTrue.size(), 1);
+    assertTrue("Result of query for 'runs' type edges with delTarget set to TRUE does not contain the expected results",
+        containsEdge(edge2, runsEdgesWithDelTargetTrue));
+  }
+
+  @Test
+  @Ignore   // For now - pending some expected fixes to the Champ library.
+  public void updateEdgeTest() throws CrudException {
+
+    // Create the source vertex for the edge.
+    Vertex sourceVertex = champDao.addVertex("vserver", MapBuilder.builder()
+        .withKeyValue("O/S", "Linux")
+        .withKeyValue("version", "6.5")
+        .withKeyValue("hostname", "kll0001")
+        .build());
+
+    // Create the target vertex for the edge.
+    Vertex destVertex = champDao.addVertex("VNF", MapBuilder.builder()
+        .withKeyValue("O/S", "Linux")
+        .withKeyValue("version", "6.5")
+        .withKeyValue("hostname", "kll0002")
+        .build());
+
+    // Now, create the edge itself.
+    Edge createdEdge = champDao.addEdge("has",
+        sourceVertex,
+        destVertex,
+        MapBuilder.builder()
+            .withKeyValue("key1", "value1")
+            .withKeyValue("key2", "value2")
+            .withKeyValue("key3", "value3")
+            .build());
+
+    // Make sure the Edge returned from the create method includes an id that we can
+    // use to retrieve it.
+    assertTrue("No valid id returned for the created edge", createdEdge.getId().isPresent());
+
+    // Retrieve the properties map for our edge and make some changes.
+    Map<String, Object> properties = createdEdge.getProperties();
+    properties.put("key4", "value4");
+    properties.remove("key2");
+
+    // Now update the edge with the new properties map.
+    Edge updatedEdge = champDao.updateEdge(createdEdge);
+
+    assertTrue("Edge property list returned from DAO update operation did not contain all expected properties - expected: " +
+            properties.keySet() + " actual: " + updatedEdge.getProperties().keySet(),
+        updatedEdge.getProperties().keySet().containsAll(properties.keySet()));
+
+    // Validate that the values assigned to the properties in the updated Edge
+    // match the ones that we provided when we created it.
+    for (String propertyKey : properties.keySet()) {
+
+      assertTrue("Unexpected value for property '" + propertyKey + "' - Expected: " +
+              properties.get(propertyKey) + "  Actual: " +
+              updatedEdge.getProperties().get(propertyKey),
+          updatedEdge.getProperties().get(propertyKey).equals(properties.get(propertyKey)));
+    }
+
+    // Validate that the property that we removed is NOT in the set of properties from our
+    // updated edge.
+    // *** We will leave this validation commented out for now, as the Champ library actually
+    //     merges update properties instead of replacing them...
+//             assertFalse("Property 'key2' should no longer be associated with updated edge",
+//                                 updatedEdge.getProperties().containsKey("key2"));
+  }
+
+  @Test
+  public void deleteEdgeTest() throws CrudException {
+
+    boolean deletedEdgeNotFound = false;
+
+    // Create the source vertex for the edge.
+    Vertex sourceVertex = champDao.addVertex("vserver", MapBuilder.builder()
+        .withKeyValue("O/S", "Linux")
+        .withKeyValue("version", "6.5")
+        .withKeyValue("hostname", "kll0001")
+        .build());
+
+    // Create the target vertex for the edge.
+    Vertex destVertex = champDao.addVertex("VNF", MapBuilder.builder()
+        .withKeyValue("O/S", "Linux")
+        .withKeyValue("version", "6.5")
+        .withKeyValue("hostname", "kll0002")
+        .build());
+
+    // Now, create the edge itself.
+    Edge createdEdge = champDao.addEdge("has",
+        sourceVertex,
+        destVertex,
+        MapBuilder.builder()
+            .withKeyValue("key1", "value1")
+            .withKeyValue("key2", "value2")
+            .withKeyValue("key3", "value3")
+            .build());
+
+    // Verify that we can retrieve the edge that we just created.
+    Edge retrievedEdge = champDao.getEdge(createdEdge.getId().get(), "has");
+
+    // Now, delete it.
+    champDao.deleteEdge(createdEdge.getId().get(), "has");
+
+    // Try retrieving it again.  This time we should not find it.
+    try {
+      champDao.getEdge(createdEdge.getId().get(), "has");
+    } catch (CrudException e) {
+
+      assertTrue(e.getMessage().contains("No edge with id"));
+      deletedEdgeNotFound = true;
+    }
+
+    assertTrue("Should not have been able to retrieve deleted edge.", deletedEdgeNotFound);
+  }
+
+  private boolean containsEdge(Edge anEdge, List<Edge> edges) {
+
+    for (Edge e : edges) {
+      if (e.getId().isPresent() && anEdge.getId().isPresent() && (e.getId().get().equals(anEdge.getId().get()))) {
+        return true;
+      }
+
+    }
+    return false;
+  }
+
+  public static class MapBuilder {
+
+    private Map<String, Object> map;
+
+    private MapBuilder() {
+      map = new HashMap<String, Object>();
+    }
+
+    public static MapBuilder builder() {
+      return new MapBuilder();
+    }
+
+    public MapBuilder withKeyValue(String key, Object value) {
+      map.put(key, value);
+      return this;
+    }
+
+    public Map<String, Object> build() {
+      return map;
+    }
+  }
+}
diff --git a/src/test/java/org/openecomp/schema/OxmModelLoaderTest.java b/src/test/java/org/openecomp/schema/OxmModelLoaderTest.java
new file mode 100644 (file)
index 0000000..bf4d871
--- /dev/null
@@ -0,0 +1,22 @@
+package org.openecomp.schema;
+
+import org.junit.Test;
+import org.openecomp.crud.exception.CrudException;
+
+import static org.junit.Assert.assertTrue;
+
+
+public class OxmModelLoaderTest {
+
+  @Test
+  public void loadModels() {
+    try {
+      OxmModelLoader.loadModels();
+    } catch (CrudException e) {
+      e.printStackTrace();
+      assertTrue(false);
+    }
+
+    assertTrue(OxmModelLoader.getVersionContextMap().size() == 4);
+  }
+}
diff --git a/src/test/resources/gremlin/gremlinResponseEmptyVertex.json b/src/test/resources/gremlin/gremlinResponseEmptyVertex.json
new file mode 100644 (file)
index 0000000..0e4b9f6
--- /dev/null
@@ -0,0 +1,15 @@
+{
+  "requestId": "54275f43-4cf5-491d-ad53-a9dada9c0fbf",
+  "status": {
+    "message": "",
+    "code": 200,
+    "attributes": {
+    }
+  },
+  "result": {
+    "data": [
+    ],
+    "meta": {
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/test/resources/gremlin/gremlinResponseMultipleVertex.json b/src/test/resources/gremlin/gremlinResponseMultipleVertex.json
new file mode 100644 (file)
index 0000000..5809be3
--- /dev/null
@@ -0,0 +1,108 @@
+{
+    "requestId":"54275f43-4cf5-491d-ad53-a9dada9c0fbf",
+    "status":{
+        "message":"",
+        "code":200,
+        "attributes":{
+        }
+    },
+    "result":{
+        "data":[
+            {
+                "id":100,
+                "label":"vertex",
+                "type":"vertex",
+                "properties":{
+                    "aai-last-mod-ts":[
+                        {
+                            "id":"j2mu-cwq8-sl",
+                            "value":1477013842
+                        }
+                    ],
+                    "speed-value":[
+                        {
+                            "id":"j312-cwq8-rnp",
+                            "value":"50000000"
+                        }
+                    ],
+                    "source-of-truth":[
+                        {
+                            "id":"j5ee-cwq8-6bp",
+                            "value":"SOT2"
+                        }
+                    ],
+                    "aai-node-type":[
+                        {
+                            "id":"j5sm-cwq8-8p1",
+                            "value":"physical-link"
+                        }
+                    ]
+                }
+            },
+            {
+                "id":200,
+                "label":"vertex",
+                "type":"vertex",
+                "properties":{
+                    "aai-last-mod-ts":[
+                        {
+                            "id":"j2mu-cwq8-sl",
+                            "value":1477013842
+                        }
+                    ],
+                    "speed-value":[
+                        {
+                            "id":"j312-cwq8-rnp",
+                            "value":"10000000"
+                        }
+                    ],
+                    "source-of-truth":[
+                        {
+                            "id":"j5ee-cwq8-6bp",
+                            "value":"RCT"
+                        }
+                    ],
+                    "aai-node-type":[
+                        {
+                            "id":"j5sm-cwq8-8p1",
+                            "value":"physical-link"
+                        }
+                    ]
+                }
+            },
+            {
+                "id":300,
+                "label":"vertex",
+                "type":"vertex",
+                "properties":{
+                    "aai-last-mod-ts":[
+                        {
+                            "id":"j2mu-cwq8-sl",
+                            "value":1477013842
+                        }
+                    ],
+                    "speed-value":[
+                        {
+                            "id":"j312-cwq8-rnp",
+                            "value":"9999"
+                        }
+                    ],
+                    "source-of-truth":[
+                        {
+                            "id":"j5ee-cwq8-6bp",
+                            "value":"RCT"
+                        }
+                    ],
+                    "aai-node-type":[
+                        {
+                            "id":"j5sm-cwq8-8p1",
+                            "value":"physical-link"
+                        }
+                    ]
+                }
+            }  
+        ],
+        "meta":{
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/resources/gremlin/gremlinResponseVertex.json b/src/test/resources/gremlin/gremlinResponseVertex.json
new file mode 100644 (file)
index 0000000..2e3a290
--- /dev/null
@@ -0,0 +1,76 @@
+{
+    "requestId":"54275f43-4cf5-491d-ad53-a9dada9c0fbf",
+    "status":{
+        "message":"",
+        "code":200,
+        "attributes":{
+        }
+    },
+    "result":{
+        "data":[
+            {
+                "id":602288,
+                "label":"vertex",
+                "type":"vertex",
+                "properties":{
+                    "aai-last-mod-ts":[
+                        {
+                            "id":"j2mu-cwq8-sl",
+                            "value":1477013842
+                        }
+                    ],
+                    "speed-value":[
+                        {
+                            "id":"j312-cwq8-rnp",
+                            "value":"10000000"
+                        }
+                    ],
+                    "link-name":[
+                        {
+                            "id":"j3fa-cwq8-u11",
+                            "value":"ldouk116sd9:vmnic11-6/2|ldouktax102:xe-1/0/66"
+                        }
+                    ],
+                    "resource-version":[
+                        {
+                            "id":"j3ti-cwq8-1l1",
+                            "value":"1477013842"
+                        }
+                    ],
+                    "speed-units":[
+                        {
+                            "id":"j47q-cwq8-t8l",
+                            "value":"kbps"
+                        }
+                    ],
+                    "aai-created-ts":[
+                        {
+                            "id":"j4ly-cwq8-4qt",
+                            "value":1476927322
+                        }
+                    ],
+                    "last-mod-source-of-truth":[
+                        {
+                            "id":"j506-cwq8-5j9",
+                            "value":"RCT"
+                        }
+                    ],
+                    "source-of-truth":[
+                        {
+                            "id":"j5ee-cwq8-6bp",
+                            "value":"RCT"
+                        }
+                    ],
+                    "aai-node-type":[
+                        {
+                            "id":"j5sm-cwq8-8p1",
+                            "value":"physical-link"
+                        }
+                    ]
+                }
+            }
+        ],
+        "meta":{
+        }
+    }
+}
\ No newline at end of file
diff --git a/version.properties b/version.properties
new file mode 100644 (file)
index 0000000..325432a
--- /dev/null
@@ -0,0 +1,13 @@
+# Versioning variables
+# Note that these variables cannot be structured (e.g. : version.release or version.snapshot etc... )
+# because they are used in Jenkins, whose plug-in doesn't support
+
+major=1
+minor=1
+patch=0
+
+base_version=${major}.${minor}.${patch}
+
+# Release must be completed with git revision # in Jenkins
+release_version=${base_version}
+snapshot_version=${base_version}-SNAPSHOT
\ No newline at end of file