diff options
68 files changed, 12628 insertions, 0 deletions
diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..da0f26d7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# https://help.github.com/articles/dealing-with-line-endings/ + +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized +# and converted to native line endings on checkout. +*.css text +*.htm text diff=html +*.html text diff=html +*.java text diff=java +*.js text +*.jsp text +*.less text +*.properties text +*.sql text +*.xml text + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary + diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..b1c3835b --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# ignore all bin directories +# matches "bin" in any subfolder +bin/ +# ignore all target directories +target/ +src/main/webapp +# ignore all files ending with ~ +*~ +.DS_Store +._* +.metadata +# Eclipse stuff +/.settings/ +.classpath +.project +# Idea +.idea +debug-logs/ +/logs/ diff --git a/.gitreview b/.gitreview new file mode 100644 index 00000000..70ebed79 --- /dev/null +++ b/.gitreview @@ -0,0 +1,4 @@ +[gerrit] +host=gerrit.acumos.org +port=29418 +project=music.git diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..cc6cdea5 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,24 @@ + +The following license applies to all files in this and sub-directories. Licenses +are included in individual source files where appropriate, and if it differs +from this text, it supersedes this. Any file that does not have license text +defaults to being covered by this text; not all files support the addition of +licenses. +# +# ------------------------------------------------------------------------- +# Copyright (c) 2017 AT&T Intellectual Property +# +# 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. +# +# ------------------------------------------------------------------------- +#
\ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..3f83f2da --- /dev/null +++ b/README.md @@ -0,0 +1,385 @@ +## MUSIC - Multi-site State Coordination Service + +To achieve 5 9s of availability on 3 9s or lower software and infrastructure in a cost-effective manner, ONAP components need to work in a reliable, active-active manner across multiple sites (platform-maturity resiliency level 3). A fundamental aspect of this is state management across geo-distributed sites in a reliable, scalable, highly available and efficient manner. This is an important and challenging problem because of three fundamental reasons: + +* Current solutions for state-management of ONAP components like MariaDB clustering, that work very effectively within a site, may not scale across geo-distributed sites (e.g., Beijing, Amsterdam and Irvine) or allow partitioned operation (thereby compromising availability). This is mainly because WAN latencies are much higher across sites and frequent network partitions can occur. + +* ONAP components often have a diverse range of requirements in terms of state replication. While some components need to synchronously manage state across replicas, others may tolerate asynchronous replication. This diversity needs to be leveraged to provide better performance and higher availability across sites. + +* ONAP components often need to partition state across different replicas, perform consistent operations on them and ensure that on failover, the new owner has access to the latest state. The distributed protocols to achieve such consistent ownership is complex and replete with corners cases, especially in the face of network partitions. Currently, each component is building its own handcrafted solution which is wasteful and worse, can be erroneous. + +In this project, we identify common state management concerns across ONAP components and provide a multi-site state coordination/management service (MUSIC) with a rich suite of recipes that each ONAP component can simply configure and use for their state-management needs. + +--- + +[Local Installation](#local-install) + +[Multi-site Installation](#ms-install) + +[Muting MUSIC jersey output](#jersey-mute) + +[Authentications/AAF Setup](#auth-setup) + + +Prerequisites + +MUSIC is to be installed in a single Dir. + +```bash +#The main MUSIC dir should be +/opt/app/music +# These also need to be set up +/opt/app/music/etc +/opt/app/music/logs +``` +When installing Tomcat, Cassandra and Zookeeper they should also be installed here. +```bash +/opt/app/music/apache-cassandra-n.n.n +/opt/app/music/zookeeper-n.n.n +/opt/app/music/apache-tomcat-n.n.n +``` +Its suggested you create links from install dirs to a common name ie: +```bash +ln -s /opt/app/music/apache-cassandra-n.n.n cassandra +ln -s /opt/app/music/zookeeper-n.n.n zookeeper +ln -s /opt/app/music/apache-tomcat-n.n.n tomcat +``` + +Cassandra and Zookeeper have data dirs. +```bash +# For cassandra it should be (This is the default) +/opt/app/music/cassandra/data +# For Zookeeper it should be +/opt/app/music/var/zookeeper/ +``` + +If you are using a VM make sure it has at least 8 GB of RAM (It may work with 4 GB, but with 2 GB it +does give issues). + +<a name="local-install"> + +## Local Installation + +</a> + +### Instructions + +- Create MUSIC Install dir /opt/app/music +- Open /etc/hosts as sudo and enter the name of the vm alongside localhost in the line for 127.0.0.1. E.g. 127.0.0.1 localhost music-1. Some of the apt-get installation seem to require this. +- Ensure you have java jdk 8 or above working on your machine. +- Download apache Apache Cassandra 3.2, install into /opt/app/music and follow these instructions http://cassandra.apache.org/doc/latest/getting_started/installing.html till and including Step +- By the end of this you should have Cassandra working. +- Download Apache Zookeeper 3.4.6, install into /opt/app/music and follow these instructions https://zookeeper.apache.org/doc/trunk/zookeeperStarted.html pertaining to the standalone operation. By the end of this you should have Zookeeper working. +- Create a music.properties file and place it in /opt/app/music/etc/. Here is a sample of the file: + +```properties +my.id=0 +all.ids=0 +my.public.ip=localhost +all.public.ips=localhost +####################################### +# Optional current values are defaults +####################################### +#zookeeper.host=localhost +#cassandra.host=localhost +#music.ip=localhost +#debug=true +#music.rest.ip=localhost +#lock.lease.period=6000 +cassandra.user=cassandra +cassandra.password=cassandra +aaf.endpoint.url=http://aafendpoint/proxy/authz/nss/ +``` + +- Make a dir /opt/app/music/logs MUSIC dir with MUSIC logs will be created in this dir after MUSIC starts. +- Download the latest Apache Tomcat and install it using these instructions http://tecadmin.net/install-tomcat-9-on-ubuntu/ (this is for version 9). +- Build the MUSIC.war (or download it from https://github.com/att/music/blob/master/MUSIC.war) and place it within the webapps folder of the tomcat installation. +- Authentications/AAF Setup For Authentication setup. +- Start tomcat and you should now have MUSIC running. + + +Extra Cassandra information for Authentication: + +To create first user in Cassandra + +Edit conf/Cassandra.yaml file + +```yaml +authenticator: PasswordAuthenticator +authorizer: CassandraAuthorizer +``` +Restart Cassandra +Login to cqlsh with default credentials + +```bash +cqlsh -u cassandra -p cassandra +``` + +To change default user create new user with the following command. + +```sql +CREATE USER new_user WITH PASSWORD ‘new_password’ SUPERUSER; +``` +Change password for default user ‘Cassandra’ so that no one will be able to login + +```sql +ALTER USER cassandra WITH PASSWORD ‘SomeLongRandomStringNoOneWillThinkOf’; +``` +Provide the new user credentials to Music. Update music.properties file and uncomment or add the following: + +```properties +cassandra.user=<new_user> +cassandra.password=<new_password> +``` + +To access keyspace through cqlsh, login with credentials that are passed to MUSIC while creating the keyspace. + + + +<a name ="ms-install"> + +## Multi-site Installation + +</a> + +Follow the instructions for local MUSIC installation on all the machines/VMs/hosts (referred to as a node) on which you want MUSIC installed. However, Cassandra and Zookeeper needs to be configured to run as multi-node installations (instructions below) before running them. + +#### Cassandra: +In the cassandra.yaml file which is present in the cassa_install/conf directory in each node, set the following parameters: +cassandra.yaml +```yaml +cluster_name: ‘name of cluster’ +num_tokens: 256 +seed_provider: +class_name: org.apache.cassandra.locator.SimpleSeedProvider +parameters: +seeds: "<public ip of first seed>, <public ip of second seed>, etc" +listen_address: private ip of VM +broadcast_address: public ip of VM +endpoint_snitch: GossipingPropertyFileSnitch +rpc_address: <private ip> +phi_convict_threshold: 12 +authenticator: PasswordAuthenticator +authorizer: CassandraAuthorizer +``` + + +In the cassandra-rackdc.properties file, assign data center and rack names as if required. + +Once this is done on all three nodes, you can run cassandra on each of the nodes through the cassandra bin folder with this command +```bash +./cassandra +``` + In the cassandra bin folder, if you run the following it will tell you the state of the cluster. +```bash +./nodetool status +``` +To access cassandra, one any of the nodes you can run the following and then perform CQL queries. +```bash +./cqlsh <private ip> +``` + +To create first user in Cassandra + + Restart Cassandra + Login to cqlsh with default credentials + +```bash +cqlsh -u cassandra -p cassandra +``` + +To change default user create new user with the following command. + +```sql +CREATE USER new_user WITH PASSWORD ‘new_password’ SUPERUSER; +``` +Change password for default user ‘Cassandra’ so that no one will be able to login + +```sql +ALTER USER cassandra WITH PASSWORD ‘SomeLongRandomStringNoOneWillThinkOf’; +``` +Provide the new user credentials to Music. Update music.properties file and uncomment or add the following: + +```properties +cassandra.user=<new_user> +cassandra.password=<new_password> +``` + +To access keyspace through cqlsh, login with credentials that are passed to MUSIC while creating the keyspace. + +#### Zookeeper: + +Once zookeeper has been installed on all the nodes, modify the zk_install_location/conf/zoo.cfg on all the nodes with the following lines: +```properties +tickTime=2000 +dataDir=/opt/app/music/var/zookeeper +clientPort=2181 +initLimit=5 +syncLimit=2 +quorumListenOnAllIPs=true +server.1=public IP of node 1:2888:3888 +server.2=public IP of node 2:2888:3888 +server.3=public IP of node 3:2888:3888 +``` +In /opt/app/music/var/zookeeper in all the machines, create a file called myid that contains the id of the machine. The machine running node.i will contain just the number i in the file myid. + +Start each of the nodes one by one from the zk_install_location/bin folder using the command: +```bash +./zkServer.sh start +``` +On each node check the file zookeeper.out in the zk_install_location/ bin to make sure all the machines are talking to each other and there are no errors. Note that while the machines are yet to come up there maybe error messages saying that connection has not yet been established. Clearly, this is ok. + +If there are no errors, then from zk_install_location/bin simply run the following to get command line access to zookeeper. ./zkCli.sh + +Run these commands on different machines to make sure the zk nodes are syncing. +```bash +[zkshell] ls / +[zookeeper] +``` +Next, create a new znode by running +```bash +create /zk_test my_data. +``` +This creates a new znode and associates the string "my_data" with the node. You should see: +```bash +[zkshell] create /zk_test my_data +Created /zk_test +``` +Issue another ls / command to see what the directory looks like: +```bash +[zkshell] ls / +[zookeeper, zk_test] +``` + +#### MUSIC + +Create a music.properties file and place it in /opt/app/music/etc at each node. Here is a sample of the file: If this location is to be changed please update the file project.properties in the src/main/resources directory before compiling MUSIC to a war. +```properties +my.id=0 +all.ids=0:1:2 +my.public.ip=public IP of node 0 +#For each node, a separate file needs to be created with its own id (between 0 and the number of nodes) and with information about its own public ip. +all.public.ips=public IP of node 0:public IP of node 1:public IP of node 2 +####################################### +# Optional current values are defaults +####################################### +#zookeeper.host=localhost +#cassandra.host=localhost +#music.ip=localhost +#debug=true +#music.rest.ip=localhost +#lock.lease.period=6000 +cassandra.user=cassandra +cassandra.password=cassandra +aaf.endpoint.url=http://aafendpoint/proxy/authz/nss/ +``` + +- Download the latest Apache Tomcat and install it using these instructions http://tecadmin.net/install-tomcat-9-on-ubuntu/ (this is for version 9). +- Build the MUSIC.war and place it within the webapps folder of the tomcat installation. +- Start tomcat and you should now have MUSIC running. + +- For Logging create a dir /opt/app/music/logs. When MUSIC/Tomcat starts a dir opt/app/music/logsMUSIC with various logs will be created. + The application log is music.log and the others are debug.log and error.log. + +<a name="jersey-mute"> + +### Muting MUSIC jersey output + +The jersey package that MUSIC uses to parse REST calls prints out the entire header and json body by +default. To mute it (if it exists), remove the following lines from the web.xml in the WEB_INF foler: + +```xml +<init-param> + <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name> + <param-value>com.sun.jersey.api.container.filter.LoggingFilter</param-value> +</init-param> +``` + +<a name ="auth-setup"> + +### Authentication Setup + +MUSIC has been enhanced to support applications which are already authenticated using AAF and applications which are not authenticated using AAF. +If an application has already been using AAF, it should have required namespace, mechId and password. Non AAF applications will be provided with a random unique id. MUSIC will tag the keyspace with the UID and store internally and any calls to use/modify the keyspace will require the UID. +All the required params should be sent as headers. + +- MUSIC has been enhanced to support applications which are already authenticated using AAF and applications which are not authenticated using AAF. +- If an application has already been using AAF, it should have required namespace, mechId and password. +- Non AAF applications (AID) will be provided with a random unique id. MUSIC will tag the keyspace with the UID and store internally and any calls to use/modify the keyspace will require the UID. + +All the required params should be sent as headers. + +Admin needs to create the following keyspace and table via cslsh. + +In the cassandra bin dir run ./cqlsh -u <user> -p <password> and log in to db then: + +If you want to save the following in a file you can then run ./cqlsh -f <file.cql> +For Single install: +```sql +//Create Admin Keyspace + +CREATE KEYSPACE admin + WITH REPLICATION = { + 'class' : 'SimpleStrategy', + 'replication_factor': 1 + } + AND DURABLE_WRITES = true; + +CREATE TABLE admin.keyspace_master ( + uuid uuid, + keyspace_name text, + application_name text, + is_api boolean, + password text, + username text, + is_aaf boolean, + PRIMARY KEY (uuid) +); +``` +Multi-Site Install: +```sql +//Create Admin Keyspace + +CREATE KEYSPACE admin + WITH REPLICATION = { + 'class' : 'NetworkTopologyStrategy', + 'DC1':2, + 'DC2':2, + 'DC3':2 + } + AND DURABLE_WRITES = true; + +CREATE TABLE admin.keyspace_master ( + uuid uuid, + keyspace_name text, + application_name text, + is_api boolean, + password text, + username text, + is_aaf boolean, + PRIMARY KEY (uuid) +); +``` + +Headers: +- For AAF applications all the 3 of the following headers are mandatory. + - <b>ns</b> + - <b>mechId</b> + - <b>password</b> +- For Non AAF applications if the header <b>aid</b> is not provided MUSIC creates new random unique UUID and returns to caller. +- Caller application then need to save the UUID and need to pass the UUID to further modify/access the keyspace. +- Make sure steps to set up Cassandra for authentication above in the cassandra.yaml were done. + + +### On Boarding app +The following API is used to onboard the applications. +/MUSIC/rest/v2/admin/onboardAppWithMusic +This is a POST call and requires the following JSON: +```json +{ +"appname": "<the Namespace for aaf or the Identifier for the specific app using AID access>", +"userId" : "<userid>", +"isAAF" : true|false +} +``` + diff --git a/WebContent/META-INF/MANIFEST.MF b/WebContent/META-INF/MANIFEST.MF new file mode 100644 index 00000000..254272e1 --- /dev/null +++ b/WebContent/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/WebContent/WEB-INF/music.properties.sample b/WebContent/WEB-INF/music.properties.sample new file mode 100644 index 00000000..d562429b --- /dev/null +++ b/WebContent/WEB-INF/music.properties.sample @@ -0,0 +1,19 @@ +#Modify this sample as required and place it as /etc/music/music.properties +my.id=0 +all.ids=0:1:2 +my.public.ip=12.13.14.45 +all.public.ips=12.13.14.45:12.13.14.46:12.13.14.47 +####################################### +# Optional current values are defaults +####################################### +#zookeeper.host=localhost +#cassandra.host=localhost +#music.ip=localhost +#debug=true +#music.rest.ip=localhost +#lock.lease.period=6000 +# Cassandra Username and password +cassandra.user=cassandra +cassandra.password=cassandra +# AAF Endpoint +aaf.endpoint.url=http://aafendpoint/proxy/authz/nss/
\ No newline at end of file diff --git a/WebContent/WEB-INF/web.xml b/WebContent/WEB-INF/web.xml new file mode 100644 index 00000000..6d8e67c9 --- /dev/null +++ b/WebContent/WEB-INF/web.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> + <display-name>MUSIC</display-name> + <servlet> + <servlet-name>music-servlet</servlet-name> + <servlet-class> + com.sun.jersey.spi.container.servlet.ServletContainer + </servlet-class> + <!-- this enables jersey jackson logging. + <init-param> + <param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name> + <param-value>com.sun.jersey.api.container.filter.LoggingFilter</param-value> + </init-param> + --> + <init-param> + <param-name>com.sun.jersey.config.property.packages</param-name> + <param-value> + io.swagger.jaxrs.json, + io.swagger.jaxrs.listing, + org.onap.music.rest + </param-value> + </init-param> + <init-param> + <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name> + <param-value>true</param-value> + </init-param> + <load-on-startup>1</load-on-startup> + + </servlet> + + <servlet> + <servlet-name>Jersey2Config</servlet-name> + <servlet-class>io.swagger.jaxrs.config.DefaultJaxrsConfig</servlet-class> + <init-param> + <param-name>api.version</param-name> + <param-value>1.0.0</param-value> + </init-param> + <init-param> + <param-name>swagger.api.basepath</param-name> + <param-value>http://localhost:8080/MUSIC/rest</param-value> + </init-param> + <load-on-startup>2</load-on-startup> + </servlet> + + + <servlet-mapping> + <servlet-name>music-servlet</servlet-name> + <url-pattern>/rest/*</url-pattern> + </servlet-mapping> + + <welcome-file-list> + <welcome-file>index.html</welcome-file> + <welcome-file>index.htm</welcome-file> + <welcome-file>index.jsp</welcome-file> + <welcome-file>default.html</welcome-file> + <welcome-file>default.htm</welcome-file> + <welcome-file>default.jsp</welcome-file> + </welcome-file-list> +</web-app> +<!-- + ============LICENSE_START========================================== + org.onap.music + =================================================================== + Copyright (c) 2017 AT&T Intellectual Property + =================================================================== + 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============================================= + ==================================================================== +--> diff --git a/pom.xml b/pom.xml new file mode 100755 index 00000000..2e44a02f --- /dev/null +++ b/pom.xml @@ -0,0 +1,281 @@ +<!-- + ============LICENSE_START========================================== + org.onap.music + =================================================================== + Copyright (c) 2017 AT&T Intellectual Property + =================================================================== + 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============================================= + ==================================================================== +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.onap.music</groupId> + <artifactId>MUSIC</artifactId> + <packaging>war</packaging> + <version>2.4.4</version> + <description> + This is the MUSIC REST interface, packaged as a war file. + </description> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <jersey1.version>1.9</jersey1.version> + <jersey2.version>2.26</jersey2.version> + <jaxrs.version>2.0.1</jaxrs.version> + <cassandra.version>3.0.0</cassandra.version> + <zookeeper.version>3.4.6</zookeeper.version> + + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <repositories> + <repository> + <id>maven2-repository.java.net</id> + <name>Java.net Repository for Maven</name> + <url>http://download.java.net/maven/2/</url> + <layout>default</layout> + </repository> + </repositories> + <build> + <finalName>MUSIC</finalName> + <sourceDirectory>src/main/java</sourceDirectory> + <outputDirectory>src/main/webapp/WEB-INF/classes</outputDirectory> + <testSourceDirectory>src/test/java</testSourceDirectory> + <testOutputDirectory>target/test-classes</testOutputDirectory> + <defaultGoal>validate</defaultGoal> + <resources> + <resource> + <directory>src/main/resources</directory> + <filtering>true</filtering> + </resource> + </resources> + <plugins> + <plugin> + <artifactId>maven-eclipse-plugin</artifactId> + <version>2.9</version> + <configuration> + <additionalProjectnatures> + <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature> + </additionalProjectnatures> + <additionalBuildcommands> + <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand> + </additionalBuildcommands> + <downloadSources>true</downloadSources> + <downloadJavadocs>true</downloadJavadocs> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.5.1</version> + <configuration> + <source>1.7</source> + <target>1.7</target> + </configuration> + </plugin> + <plugin> + <artifactId>maven-war-plugin</artifactId> + <version>2.4</version> + <configuration> + <warSourceDirectory>WebContent</warSourceDirectory> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <version>2.4</version> + <executions> + <execution> + <id>make-a-jar</id> + <phase>compile</phase> + <goals> + <goal>jar</goal> + </goals> + <configuration> + <excludes> + <exclude>**/*.xml</exclude> + <exclude>**/*.properties</exclude> + <exclude>**/Sample*</exclude> + </excludes> + </configuration> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-install-plugin</artifactId> + <version>2.4</version> + <executions> + <execution> + <phase>install</phase> + <goals> + <goal>install-file</goal> + </goals> + <configuration> + <packaging>jar</packaging> + <artifactId>${project.artifactId}</artifactId> + <groupId>${project.groupId}</groupId> + <version>${project.version}</version> + <file>${project.build.directory}/${project.artifactId}.jar</file> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <dependencies> + + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-core</artifactId> + <version>1.1.3</version> + + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + </exclusion> + </exclusions> + </dependency> + + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + <version>1.1.3</version> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + </exclusion> + </exclusions> + </dependency> + + <dependency> + <groupId>com.datastax.cassandra</groupId> + <artifactId>cassandra-driver-core</artifactId> + <version>${cassandra.version}</version> + </dependency> + <dependency> + <groupId>org.cassandraunit</groupId> + <artifactId>cassandra-unit</artifactId> + <version>3.3.0.1</version> + </dependency> + <dependency> + <groupId>org.apache.zookeeper</groupId> + <artifactId>zookeeper</artifactId> + <version>${zookeeper.version}</version> + </dependency> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-client</artifactId> + <version>${jersey1.version}</version> + </dependency> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-server</artifactId> + <version>${jersey1.version}</version> + </dependency> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-json</artifactId> + <version>${jersey1.version}</version> + </dependency> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>servlet-api</artifactId> + <version>2.4</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-model</artifactId> + <version>3.3.9</version> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-jcs-core</artifactId> + <version>2.2</version> + </dependency> + + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>javax.servlet-api</artifactId> + <version>3.0.1</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>commons-codec</groupId> + <artifactId>commons-codec</artifactId> + <version>1.11</version> + </dependency> + + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + <version>1.9.0</version> + </dependency> + + + <dependency> + <groupId>io.swagger</groupId> + <artifactId>swagger-jersey-jaxrs</artifactId> + <version>1.5.0</version> + </dependency> + + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>19.0</version> + </dependency> + + <dependency> + <groupId>com.att.eelf</groupId> + <artifactId>eelf-core</artifactId> + <version>1.0.0</version> + </dependency> + </dependencies> + + <reporting> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>2.10.3</version> + <configuration> + <author>true</author> + <breakiterator>true</breakiterator> + <version>true</version> + <links> + <link>https://docs.oracle.com/javase/7/docs/api/</link> + <link>https://tomcat.apache.org/tomcat-7.0-doc/jspapi/</link> + <link>http://docs.oracle.com/javaee/7/api/</link> + </links> + </configuration> + </plugin> + </plugins> + </reporting> + +</project> diff --git a/postman/MusicLocal.postman_collection.json b/postman/MusicLocal.postman_collection.json new file mode 100644 index 00000000..0fef0bb9 --- /dev/null +++ b/postman/MusicLocal.postman_collection.json @@ -0,0 +1,695 @@ +{ + "id": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "name": "MusicLocal", + "description": "", + "order": [ + "cde8166d-a97c-c173-c00a-7584cb8d24ea", + "9a8091d5-1207-d121-e723-1c927253d986", + "6309194d-0855-d97c-5d92-5c91860cfa9f", + "695ae5ac-9db1-873d-0733-49885c060605", + "59a33710-2189-ee04-66f1-630920300fdf", + "272e9995-c6cd-a456-57a5-f41ca424a2a0", + "b7e1120b-0c82-2e73-b3a8-c3c9d9662c7f", + "eff968ca-84d1-54a2-ce26-0f9c95bf965d", + "fc561465-a6d2-1fd8-ecb5-3c88738fd219", + "fe7680e6-8407-e2e1-f89c-763c255b3538", + "b5ecf9b0-9bac-3099-e292-541a7d814a91", + "6a6dc6ca-c413-a8da-5f9d-31ea88da7767", + "517e24a4-db48-8a36-6387-ae71478b5e1c", + "c2416eb6-8ba5-3d5b-6585-0e070cf032fc", + "f096757d-ebe6-6126-633a-99a684a0e705", + "f9212734-54e2-4fe5-09c0-c67bed88770a", + "d7c46575-d1a2-7b85-aad2-f43728a077f0", + "21667ae7-0272-5acc-cc54-dd1221f2fece", + "feed66c2-797e-c469-3c8a-f555b92d22d4", + "9cc7f611-8c6f-f564-ef9a-53aecaaf0837", + "04cba481-f6bf-6d53-ef8c-543b9541ff1a", + "b667b219-8eec-2267-1994-7f0fb97b77ce", + "28dd3c51-1c0c-65c0-4037-bda04c6a05b7", + "d3d7de68-3af7-1d52-1575-3e579949e484", + "f895d276-f717-afc8-7bea-6ae881c74aeb", + "89d85877-b30e-dc5a-7bee-90e375a2ff78", + "200b3cae-32cb-fabe-a86b-f8f1bfdec9e0", + "6954fd5e-8f5e-5f46-c11c-2aa209ee98d1", + "4c425caa-f06b-8978-f7af-676d0e28073b", + "b33314ff-f0df-7f98-a594-c0b2a60902e1", + "4c652cee-d1b1-613d-3b14-4c8a52c3c2dd", + "ada66950-0758-54c9-17fe-1dae95c7f67a", + "5eb909b0-6c7d-972e-b601-f26a0b990ba1" + ], + "folders": [], + "timestamp": 1443818487905, + "owner": 0, + "remoteLink": "", + "public": false, + "requests": [ + { + "id": "04cba481-f6bf-6d53-ef8c-543b9541ff1a", + "headers": "Content-Type: application/json\n", + "url": "http://localhost:8080/MUSIC/rest/v2/keyspaces/testks/tables/projects", + "preRequestScript": "", + "pathVariables": {}, + "method": "DELETE", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1455129844265, + "name": "Drop table projects (atomic)", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "{\n \"consistencyInfo\": {\n \"type\": \"atomic\",\n \"lockId\":\"$testks.projects$x-95351186188533762-0000000000\"\n } \n}" + }, + { + "id": "200b3cae-32cb-fabe-a86b-f8f1bfdec9e0", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/locks/release/$testks.projects$x-95351186188533762-0000000000", + "preRequestScript": "", + "pathVariables": {}, + "method": "DELETE", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1455130135037, + "name": "Release \"testks.projects\"", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "" + }, + { + "id": "21667ae7-0272-5acc-cc54-dd1221f2fece", + "headers": "Content-Type: application/json\n", + "url": "http://localhost:8080/MUSIC/rest/v2/keyspaces/testks/tables/employees/rows?emp_name=joe", + "preRequestScript": "", + "pathVariables": {}, + "method": "DELETE", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454945571791, + "name": "Delete column value (eventual) ", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "{\n \"columns\": [\"emp_salary\", \"address\"],\n \"consistencyInfo\": {\n \"type\": \"eventual\"\n }\n}" + }, + { + "id": "272e9995-c6cd-a456-57a5-f41ca424a2a0", + "headers": "Content-Type: application/json\n", + "url": "http://localhost:8080/MUSIC/rest/v2/keyspaces/testks/tables/employees", + "preRequestScript": "", + "pathVariables": {}, + "method": "POST", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454944181424, + "name": "Create new table \"testks.employees\" (eventual)", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "{\n \"fields\": {\n \"emp_id\": \"uuid\",\n \"emp_name\": \"text\",\n \"emp_salary\": \"varint\",\n \"address\": \"Map<text,text>\",\n \"PRIMARY KEY\":\"(emp_name)\"\n },\n \"properties\": {\n \"comment\": \"Financial Info of employees\",\n \"compression\":{ \"sstable_compression\" : \"DeflateCompressor\", \"chunk_length_kb\" : 64 },\n \"compaction\": { \"class\" : \"SizeTieredCompactionStrategy\", \"min_threshold\" : 6 }\n }, \n \"consistencyInfo\": {\n \"type\": \"eventual\"\n } \n}" + }, + { + "id": "28dd3c51-1c0c-65c0-4037-bda04c6a05b7", + "headers": "Content-Type: application/json\n", + "url": "http://localhost:8080/MUSIC/rest/v2/keyspaces/testmusic", + "preRequestScript": "", + "pathVariables": {}, + "method": "DELETE", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1455129913345, + "name": "Drop keyspace (atomic)", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "{\n \"consistencyInfo\": {\n \"type\": \"atomic\",\n \"lockId\": \"$testmusic$x-95351186188533762-0000000000\"\n } \n}" + }, + { + "id": "4c425caa-f06b-8978-f7af-676d0e28073b", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/locks/release/$testks.projects$x-95351186188533761-0000000000", + "preRequestScript": "", + "pathVariables": {}, + "method": "DELETE", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454945804464, + "name": "Release \"testks.employees.bharath\"", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "" + }, + { + "id": "4c652cee-d1b1-613d-3b14-4c8a52c3c2dd", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/locks/delete/testks.projects", + "preRequestScript": "", + "pathVariables": {}, + "method": "DELETE", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454957689347, + "name": "Delete lock \"testks.projects\"", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "" + }, + { + "id": "517e24a4-db48-8a36-6387-ae71478b5e1c", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/locks/create/testks.employees.bharath", + "preRequestScript": "", + "pathVariables": {}, + "method": "POST", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454945175277, + "name": "Create and acquire lock \"testks.employees.bharath\"", + "description": "the lock name should be keyspacename.tablename.keyname", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "" + }, + { + "id": "59a33710-2189-ee04-66f1-630920300fdf", + "headers": "Content-Type: application/json\n", + "url": "http://localhost:8080/MUSIC/rest/v2/keyspaces/testmusic", + "preRequestScript": "", + "pathVariables": {}, + "method": "POST", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1455129706478, + "name": "Create new keyspace \"testmusic\" (atomic)", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "{\n \"replicationInfo\": {\n \"class\": \"SimpleStrategy\",\n \"replication_factor\": 1\n },\n \"durabilityOfWrites\":\"true\",\n \"consistencyInfo\": {\n \"type\": \"atomic\",\n \"lockId\": \"$testmusic$x-95351186188533761-0000000000\"\n } \n}" + }, + { + "id": "5eb909b0-6c7d-972e-b601-f26a0b990ba1", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/locks/delete/testks.employees.bharath", + "preRequestScript": "", + "pathVariables": {}, + "method": "DELETE", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454957709344, + "name": "Delete lock \"testks.employees.bharath\"", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "" + }, + { + "id": "6309194d-0855-d97c-5d92-5c91860cfa9f", + "headers": "Content-Type: application/json\n", + "url": "http://localhost:8080/MUSIC/rest/v2/keyspaces/testks", + "preRequestScript": "", + "pathVariables": {}, + "method": "POST", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454935383882, + "name": "Create new keyspace \"testks\" (eventual)", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "{\n \"replicationInfo\": {\n \"class\": \"SimpleStrategy\",\n \"replication_factor\": 1\n },\n \"durabilityOfWrites\":\"true\",\n \"consistencyInfo\": {\n \"type\": \"eventual\"\n } \n}" + }, + { + "id": "6954fd5e-8f5e-5f46-c11c-2aa209ee98d1", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/locks/release/$testks.projects$x-95351186188533761-0000000000", + "preRequestScript": "", + "pathVariables": {}, + "method": "DELETE", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454945804464, + "name": "Release \"testks.employees\"", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "" + }, + { + "id": "695ae5ac-9db1-873d-0733-49885c060605", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/locks/create/testmusic", + "preRequestScript": "", + "pathVariables": {}, + "method": "POST", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454944173069, + "name": "Create and acquire lock named \"testmusic\"", + "description": "the lock name should be the exact same name as the keyspace being created. ", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "" + }, + { + "id": "6a6dc6ca-c413-a8da-5f9d-31ea88da7767", + "headers": "Content-Type: application/json\n", + "url": "http://localhost:8080/MUSIC/rest/v2/keyspaces/testks/tables/employees/rows?emp_name=joe", + "preRequestScript": "", + "pathVariables": {}, + "method": "PUT", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454945126893, + "name": "Update column for joe (eventual)", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "{\n \"values\": {\n \"address\":{ \"street\" : \"thomas street\", \"city\" : \"nyc\" }\n },\n \"consistencyInfo\": {\n \"type\": \"eventual\"\n }\n}" + }, + { + "id": "89d85877-b30e-dc5a-7bee-90e375a2ff78", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/locks/release/$testmusic$x-95351186188533762-0000000000", + "preRequestScript": "", + "pathVariables": {}, + "method": "DELETE", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1455130112211, + "name": "Release \"testmusic\"", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "" + }, + { + "id": "9a8091d5-1207-d121-e723-1c927253d986", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/test", + "preRequestScript": "", + "pathVariables": {}, + "method": "GET", + "data": [], + "dataMode": "params", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1450912270089, + "name": "random tests", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6" + }, + { + "id": "9cc7f611-8c6f-f564-ef9a-53aecaaf0837", + "headers": "Content-Type: application/json\n", + "url": "http://localhost:8080/MUSIC/rest/v2/keyspaces/tesks/tables/employees", + "preRequestScript": "", + "pathVariables": {}, + "method": "DELETE", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454945626279, + "name": "Drop table employees (eventual) ", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "{\n \"consistencyInfo\": {\n \"type\": \"eventual\"\n } \n}" + }, + { + "id": "ada66950-0758-54c9-17fe-1dae95c7f67a", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/locks/delete/testks.employees", + "preRequestScript": "", + "pathVariables": {}, + "method": "DELETE", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454957696653, + "name": "Delete lock \"testks.employees\"", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "" + }, + { + "id": "b33314ff-f0df-7f98-a594-c0b2a60902e1", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/locks/delete/testmusic", + "preRequestScript": "", + "pathVariables": {}, + "method": "DELETE", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454957679029, + "name": "Delete lock \"testmusic\"", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "" + }, + { + "id": "b5ecf9b0-9bac-3099-e292-541a7d814a91", + "headers": "Content-Type: application/json\n", + "url": "http://localhost:8080/MUSIC/rest/v2/keyspaces/testks/tables/employees/rows", + "preRequestScript": "", + "pathVariables": {}, + "method": "POST", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1455129742447, + "name": "Insert (bharath) into \"tesks.employees\" table (atomic)", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "{\n \"values\": {\n \"emp_id\": \"cfd66ccc-d857-4e90-b1e5-df98a3d40cd6\",\n \"emp_name\": \"bharath\",\n \"emp_salary\": 50,\n \"address\":{ \"street\" : \"att way\", \"city\" : \"bedminster\" }\n },\n \"consistencyInfo\": {\n \"type\": \"atomic\",\n \"lockId\": \"$testks.employees$x-95351186188533762-0000000000\"\n }\n}" + }, + { + "id": "b667b219-8eec-2267-1994-7f0fb97b77ce", + "headers": "Content-Type: application/json\n", + "url": "http://localhost:8080/MUSIC/rest/v2/keyspaces/testks", + "preRequestScript": "", + "pathVariables": {}, + "method": "DELETE", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454945685594, + "name": "Drop keyspace (eventual)", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "{\n \"consistencyInfo\": {\n \"type\": \"eventual\"\n } \n}" + }, + { + "id": "b7e1120b-0c82-2e73-b3a8-c3c9d9662c7f", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/locks/create/testks.projects", + "preRequestScript": "", + "pathVariables": {}, + "method": "POST", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454944356576, + "name": "Create and acquire lock \"testks.projects\"", + "description": "the lock name should be keyspacename.tablename", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "" + }, + { + "id": "c2416eb6-8ba5-3d5b-6585-0e070cf032fc", + "headers": "Content-Type: application/json\n", + "url": "http://localhost:8080/MUSIC/rest/v2/keyspaces/testks/tables/employees/rows?emp_name=bharath", + "preRequestScript": "", + "pathVariables": {}, + "method": "PUT", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1455129766385, + "name": "Update column for bharath (atomic)", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "{\n \"values\": {\n \"emp_salary\": 1000\n },\n \"consistencyInfo\": {\n \"type\": \"atomic\",\n \"lockId\": \"$testks.employees.bharath$x-95351186188533762-0000000000\"\n }\n}" + }, + { + "id": "cde8166d-a97c-c173-c00a-7584cb8d24ea", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/version", + "preRequestScript": "", + "pathVariables": {}, + "method": "GET", + "data": [], + "dataMode": "params", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454933904557, + "name": "Version Test", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6" + }, + { + "id": "d3d7de68-3af7-1d52-1575-3e579949e484", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/locks/acquire/$testmusic$x-95351186188533762-0000000000", + "preRequestScript": "", + "pathVariables": {}, + "method": "GET", + "data": [], + "dataMode": "params", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1455129929805, + "name": "Test if you have lock", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [] + }, + { + "id": "d7c46575-d1a2-7b85-aad2-f43728a077f0", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/keyspaces/testks/tables/employees/rows", + "preRequestScript": "", + "pathVariables": {}, + "method": "GET", + "data": [], + "dataMode": "params", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454945233493, + "name": "Select all from table ", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6" + }, + { + "id": "eff968ca-84d1-54a2-ce26-0f9c95bf965d", + "headers": "Content-Type: application/json\n", + "url": "http://localhost:8080/MUSIC/rest/v2/keyspaces/testks/tables/projects", + "preRequestScript": "", + "pathVariables": {}, + "method": "POST", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1455129724221, + "name": "Create new table \"testks.projects\" (atomic) ", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "{\n \"fields\": {\n \"project_name\": \"text\",\n \"project_desc\": \"text\",\n \"PRIMARY KEY\":\"(project_name)\"\n },\n \"consistencyInfo\": {\n \"type\": \"atomic\",\n \"lockId\": \"$testks.projects$x-95351186188533762-0000000000\"\n } \n}" + }, + { + "id": "f096757d-ebe6-6126-633a-99a684a0e705", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/keyspaces/testks/tables/employees/rows?emp_name=bharath", + "preRequestScript": "", + "pathVariables": {}, + "method": "GET", + "data": [], + "dataMode": "params", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454945205888, + "name": "Select (bharath) from table", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6" + }, + { + "id": "f895d276-f717-afc8-7bea-6ae881c74aeb", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/locks/enquire/testmusic", + "preRequestScript": "", + "pathVariables": {}, + "method": "GET", + "data": [], + "dataMode": "params", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1455130102304, + "name": "Enquire about lock", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [] + }, + { + "id": "f9212734-54e2-4fe5-09c0-c67bed88770a", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/keyspaces/testks/tables/employees/rows?emp_name=joe", + "preRequestScript": "", + "pathVariables": {}, + "method": "GET", + "data": [], + "dataMode": "params", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454945215385, + "name": "Select (joe) from table ", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6" + }, + { + "id": "fc561465-a6d2-1fd8-ecb5-3c88738fd219", + "headers": "Content-Type: application/json\n", + "url": "http://localhost:8080/MUSIC/rest/v2/keyspaces/testks/tables/employees/rows", + "preRequestScript": "", + "pathVariables": {}, + "method": "POST", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454944527955, + "name": "Insert (joe) into \"testks.employees\" table (eventual)", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "{\n \"values\": {\n \"emp_id\": \"abc66ccc-d857-4e90-b1e5-df98a3d40cd6\",\n \"emp_name\": \"joe\",\n \"emp_salary\": 30\n },\n \"consistencyInfo\": {\n \"type\": \"eventual\"\n } \n}" + }, + { + "id": "fe7680e6-8407-e2e1-f89c-763c255b3538", + "headers": "", + "url": "http://localhost:8080/MUSIC/rest/v2/locks/create/testks.employees", + "preRequestScript": "", + "pathVariables": {}, + "method": "POST", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1454944949245, + "name": "Create and acquire lock \"testks.employees\"", + "description": "the lock name should be keyspacename.tablename", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "" + }, + { + "id": "feed66c2-797e-c469-3c8a-f555b92d22d4", + "headers": "Content-Type: application/json\n", + "url": "http://localhost:8080/MUSIC/rest/v2/keyspaces/testks/tables/employees/rows?emp_name=bharath", + "preRequestScript": "", + "pathVariables": {}, + "method": "DELETE", + "data": [], + "dataMode": "raw", + "version": 2, + "tests": "", + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1455129806221, + "name": "Delete column value (atomic)", + "description": "", + "collectionId": "a7ff2cf7-0a5a-1b0b-a393-829a672c80d6", + "responses": [], + "rawModeData": "{\n \"columns\": [\"emp_salary\", \"address\"],\n \"consistencyInfo\": {\n \"type\": \"atomic\",\n \"lockId\": \"$testks.employees.bharath$x-95351186188533762-0000000000\"\n }\n}" + } + ] +}
\ No newline at end of file diff --git a/src/main/java/LICENSE.txt b/src/main/java/LICENSE.txt new file mode 100644 index 00000000..cc6cdea5 --- /dev/null +++ b/src/main/java/LICENSE.txt @@ -0,0 +1,24 @@ + +The following license applies to all files in this and sub-directories. Licenses +are included in individual source files where appropriate, and if it differs +from this text, it supersedes this. Any file that does not have license text +defaults to being covered by this text; not all files support the addition of +licenses. +# +# ------------------------------------------------------------------------- +# Copyright (c) 2017 AT&T Intellectual Property +# +# 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. +# +# ------------------------------------------------------------------------- +#
\ No newline at end of file diff --git a/src/main/java/org/onap/music/client/MusicClient.java b/src/main/java/org/onap/music/client/MusicClient.java new file mode 100644 index 00000000..2d29e236 --- /dev/null +++ b/src/main/java/org/onap/music/client/MusicClient.java @@ -0,0 +1,604 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.client; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.onap.music.datastore.jsonobjects.JsonInsert; +import org.onap.music.datastore.jsonobjects.JsonKeySpace; +import org.onap.music.datastore.jsonobjects.JsonTable; +import org.onap.music.lockingservice.MusicLockingService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.ColumnDefinitions; +import com.datastax.driver.core.ColumnDefinitions.Definition; +import com.datastax.driver.core.ConsistencyLevel; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.KeyspaceMetadata; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.SimpleStatement; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.TableMetadata; +import com.datastax.driver.core.querybuilder.Clause; +import com.datastax.driver.core.querybuilder.Delete; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.querybuilder.Select; + +/** + * A MUSIC client that talks directly to Cassandra/ZooKeeper. This was taken, and slightly modified, + * from the REST version of the code. + * + * @author Robert Eby + */ +public class MusicClient { + private static final Logger LOG = LoggerFactory.getLogger(MusicClient.class); + + private final String[] music_hosts; // array of hosts in the music cluster + private Cluster cluster; // MUSIC Cassandra cluster + private Session session; // MUSIC Cassandra session + private MusicLockingService mls; // ZooKeeper + private final Set<String> lockNames;// set of active lock names + + /** + * Create a MUSIC client that talks to MUSIC on localhost. + */ + public MusicClient() { + this("127.0.0.1"); + } + + /** + * Create a MUSIC client that talks to MUSIC on a remote host. The string <i>hosts</i> is a + * comma-separated list of IP addresses for remote instances of Cassandra/ZooKeeper. + * + * @param hosts the list of hostnames + */ + public MusicClient(String hosts) { + music_hosts = hosts.split(","); + if (cluster == null) { + LOG.debug("Initializing MUSIC Client with endpoints " + hosts); + cluster = Cluster.builder().addContactPoints(music_hosts).build(); + } + session = cluster.connect(); + mls = null; + lockNames = new HashSet<String>(); + } + + /** + * Close the connection to MUSIC. + */ + public void close() { + if (session != null) { + session.close(); + session = null; + } + if (cluster != null) { + cluster.close(); + cluster = null; + } + } + + /** + * Be sure to close the connection to MUSIC when this object is GC-ed. + */ + @Override + protected void finalize() { + close(); + } + + /** + * Return a String representation of the music hosts used by this object. + * + * @return the string + */ + @Override + public String toString() { + List<String> t = Arrays.asList(music_hosts); + return "MUSIC hosts=" + t.toString(); + } + + /** + * Create a lock. + * + * @see org.onap.music.lockingservice.MusicLockingService#createLock(String) + * @param lockName the lock name + * @return FILL IN + */ + public String createLock(String lockName) { + String ln = "/" + lockName; + synchronized (lockNames) { + lockNames.add(ln); + } + return getLockingService().createLockId(ln); + } + + /** + * Acquire a lock. + * + * @see org.onap.music.lockingservice.MusicLockingService#lock(String) + * @param lockName the lock name + * @return FILL IN + */ + public boolean acquireLock(String lockName) { + return getLockingService().isMyTurn(lockName); + } + + /** + * Get the lock holder. + * + * @see org.onap.music.lockingservice.MusicLockingService#currentLockHolder(String) + * @param lockName the lock name + * @return FILL IN + */ + public String getLockHolder(String lockName) { + return getLockingService().whoseTurnIsIt("/" + lockName); + } + + /** + * Unlock a lock. + * + * @see org.onap.music.lockingservice.MusicLockingService#unlock(String) + * @param lockName the lock name + */ + public void unlockLock(String lockName) { + getLockingService().unlockAndDeleteId(lockName); + } + + /** + * Delete a lock. + * + * @see org.onap.music.lockingservice.MusicLockingService#deleteLock(String) + * @param lockName the lock name + */ + public void deleteLock(String lockName) { + String ln = "/" + lockName; + synchronized (lockNames) { + lockNames.remove(ln); + } + getLockingService().deleteLock(ln); + } + + /** + * Delete all locks. + * + * @see org.onap.music.lockingservice.MusicLockingService#deleteLock(String) + * @return true + */ + public boolean deleteAllLocks() { + synchronized (lockNames) { + for (String lockName : lockNames) { + deleteLock(lockName); + } + lockNames.clear(); + } + return true; + } + + /** + * Create a keyspace using the default replication configuration. + * + * @param keyspaceName the name of the keyspace + * @return always true currently + * @throws Exception Cassandra exceptions are passed through + */ + public boolean createKeyspace(String keyspaceName) throws Exception { + Map<String, Object> repl = new HashMap<String, Object>(); + repl.put("class", "SimpleStrategy"); + repl.put("replication_factor", 1); + Map<String, String> consistencyInfo = Collections.singletonMap("type", "eventual"); + JsonKeySpace jsonKp = new JsonKeySpace(); + jsonKp.setConsistencyInfo(consistencyInfo); + jsonKp.setDurabilityOfWrites("true"); + jsonKp.setReplicationInfo(repl); + return createKeyspace(keyspaceName, jsonKp); + } + + public boolean createKeyspace(String keyspaceName, JsonKeySpace kspObject) throws Exception { + String consistency = extractConsistencyInfo(keyspaceName, kspObject.getConsistencyInfo()); + Map<String, Object> replicationInfo = kspObject.getReplicationInfo(); + String durability = ""; + if (kspObject.getDurabilityOfWrites() != null) + durability = " AND durable_writes = " + kspObject.getDurabilityOfWrites(); + String query = String.format( + "CREATE KEYSPACE IF NOT EXISTS %s WITH replication = { %s } %s;", + keyspaceName, jsonMaptoSqlString(replicationInfo, ","), durability); + LOG.debug(query); + executeCreateQuery(query, consistency); + return true; + } + + public boolean dropKeyspace(String keyspaceName, JsonKeySpace kspObject) throws Exception { + String consistency = extractConsistencyInfo(keyspaceName, kspObject.getConsistencyInfo()); + String query = String.format("DROP KEYSPACE %s;", keyspaceName); + LOG.debug(query); + executeCreateQuery(query, consistency); + return false; + } + + public boolean createTable(String tablename, Map<String, String> cols) throws Exception { + JsonTable tableObj = new JsonTable(); + Map<String, String> map = new HashMap<String, String>(); // This should be in the + // consutructor! + map.put("type", "eventual"); + tableObj.setConsistencyInfo(map); + return createTable(tablename, cols, tableObj); + } + + public boolean createTable(String tablename, Map<String, String> cols, JsonTable tableObj) + throws Exception { + // Note: https://docs.datastax.com/en/cql/3.0/cql/cql_reference/create_table_r.html + + // first read the information about the table fields + StringBuilder fields = new StringBuilder(); + String prefix = ""; + for (String key : cols.keySet()) { + fields.append(prefix).append(key).append(" ").append(cols.get(key)); + prefix = ", "; + } + + // information about the name-value style properties + // Map<String,Object> propertiesMap = tableObj.getProperties(); + // String propertiesString=""; + // if(propertiesMap != null){ + // counter =0; + // for (Map.Entry<String, Object> entry : propertiesMap.entrySet()) + // { + // Object ot = entry.getValue(); + // String value = ot+""; + // if(ot instanceof String){ + // value = "'"+value+"'"; + // }else if(ot instanceof Map){ + // Map<String,Object> otMap = (Map<String,Object>)ot; + // value = "{"+jsonMaptoSqlString(otMap, ",")+"}"; + // } + // propertiesString = propertiesString+entry.getKey()+"="+ value+""; + // if(counter!=propertiesMap.size()-1) + // propertiesString = propertiesString+" AND "; + // counter = counter +1; + // } + // } + + String query = String.format("CREATE TABLE IF NOT EXISTS %s (%s);", tablename, + fields.toString()); + // if (propertiesMap != null) + // query = query + " WITH "+ propertiesString; + + LOG.debug(query); + String consistency = extractConsistencyInfo(tablename, tableObj.getConsistencyInfo()); + executeCreateQuery(query, consistency); + return false; + } + + public boolean dropTable(String name) { + // TODO + return false; + } + + public boolean insertRow(String name, Map<String, Object> valuesMap) throws Exception { + Map<String, String> consistencyInfo = Collections.singletonMap("type", "eventual"); + return insertRow(name, valuesMap, consistencyInfo, new JsonInsert()); + } + + public boolean insertRow(String tablename, Map<String, Object> valuesMap, + Map<String, String> consistencyInfo, JsonInsert insObj) throws Exception { + // Note: https://docs.datastax.com/en/cql/3.0/cql/cql_reference/insert_r.html + String[] parts = tablename.split("\\."); + KeyspaceMetadata ks = cluster.getMetadata().getKeyspace(parts[0]); + TableMetadata tableInfo = ks.getTable(parts[1]); + + StringBuilder fields = new StringBuilder(); + StringBuilder values = new StringBuilder(); + String prefix = ""; + for (String key : valuesMap.keySet()) { + fields.append(prefix).append(key); + Object valueObj = valuesMap.get(key); + DataType colType = tableInfo.getColumn(key).getType(); + values.append(prefix).append(convertToSqlDataType(colType, valueObj)); + prefix = ", "; + } + + String suffix = getTTLSuffix(insObj); + String query = String.format("INSERT INTO %s (%s) VALUES (%s)%s;", tablename, + fields.toString(), values.toString(), suffix); + LOG.debug(query); + + String consistency = extractConsistencyInfo(tablename, consistencyInfo); + executeCreateQuery(query, consistency); + return false; + } + + public boolean lockRow(String name, Map<String, String> cols) { + // TODO + return false; + } + + /** + * Select ALL rows in the table. + * + * @param tablename the name of the table + * @return a list of maps, one map per row + */ + public List<Map<String, Object>> selectRows(final String tablename) { + return selectRows(tablename, new HashMap<String, String>()); + } + + public List<Map<String, Object>> selectRows(final String tablename, Map<String, String> cols) { + String ns = ""; + String tbl = tablename; + int ix = tbl.indexOf('.'); + if (ix >= 0) { + ns = tablename.substring(0, ix); + tbl = tablename.substring(ix + 1); + } + Select sel = QueryBuilder.select().all().from(ns, tbl); + Statement stmt = sel; + if (cols.size() == 1) { + // only handles 1 WHERE value right now + String k = cols.keySet().iterator().next(); + Clause eqclause = QueryBuilder.eq(k, cols.get(k)); + stmt = sel.where(eqclause); + } + ResultSet resultset = session.execute(stmt); + List<Map<String, Object>> results = new ArrayList<Map<String, Object>>(); + for (Row row : resultset) { + Map<String, Object> map = new HashMap<String, Object>(); + for (Definition definition : row.getColumnDefinitions()) { + map.put(definition.getName(), + readRow(row, definition.getName(), definition.getType())); + } + results.add(map); + } + return results; + } + + private Object readRow(final Row row, final String name, final DataType colType) { + switch (colType.getName()) { + case BIGINT: + return row.getLong(name); + case BOOLEAN: + return row.getBool(name); + case DOUBLE: + return row.getDouble(name); + case FLOAT: + return row.getFloat(name); + case INT: + return row.getInt(name); + case MAP: + return row.getMap(name, String.class, String.class); + case UUID: + return row.getUUID(name); + case TEXT: + case VARCHAR: + return row.getString(name); + case VARINT: + return row.getVarint(name); + // These are not supported right now.... + // ASCII + // BLOB + // COUNTER + // CUSTOM + // DECIMAL + // INET + // LIST + // SET + // TIMESTAMP + // TIMEUUID + // TUPLE + // UDT + default: + return null; + } + } + + @Deprecated + public List<Map<String, String>> OLDselectRows(String tablename, Map<String, String> cols) { + String query = String.format("SELECT * FROM %s", tablename); + if (cols.size() > 0) { + // add WHERE clause + // String[] parts = tablename.split("\\."); + // KeyspaceMetadata ks = cluster.getMetadata().getKeyspace(parts[0]); + // TableMetadata tableInfo = ks.getTable(parts[1]); + String whereclause = " WHERE"; + String prefix = ""; + for (String key : cols.keySet()) { + String val = cols.get(key); + // DataType colType = tableInfo.getColumn(key).getType(); + whereclause = String.format("%s%s %s = '%s'", whereclause, prefix, key, val); + prefix = " AND"; + } + query += whereclause; + } + LOG.debug(query); + ResultSet resultset = session.execute(query); + List<Map<String, String>> results = new ArrayList<Map<String, String>>(); + for (Row row : resultset) { + ColumnDefinitions colInfo = row.getColumnDefinitions(); + Map<String, String> map = new HashMap<String, String>(); + for (Definition definition : colInfo) { + // map.put(definition.getName(), (String)MusicDataStore.readRow(row, + // definition.getName(), definition.getType())); + } + results.add(map); + } + return results; + } + + public void updateRows(String tablename, Map<String, String> cols, Map<String, Object> vals) + throws Exception { + Map<String, String> consistencyInfo = Collections.singletonMap("type", "eventual"); + updateRows(tablename, cols, vals, consistencyInfo, new JsonInsert()); + } + + public void updateRows(String tablename, Map<String, String> cols, Map<String, Object> vals, + Map<String, String> consistencyInfo, JsonInsert insObj) throws Exception { + // https://docs.datastax.com/en/cql/3.0/cql/cql_reference/update_r.html + + // obtain the field value pairs of the update + String[] parts = tablename.split("\\."); + KeyspaceMetadata ks = cluster.getMetadata().getKeyspace(parts[0]); + TableMetadata tableInfo = ks.getTable(parts[1]); + + StringBuilder fields = new StringBuilder(); + String prefix = ""; + for (String key : vals.keySet()) { + Object valueObj = vals.get(key); + String valueString = convertToSqlDataType(tableInfo.getColumn(key).getType(), valueObj); + fields.append(prefix).append(key).append(" = ").append(valueString); + prefix = ", "; + } + + // get the row specifier + StringBuilder rows = new StringBuilder(); + String primaryKey = ""; + prefix = ""; + for (String key : cols.keySet()) { + String indValue = cols.get(key); + DataType colType = tableInfo.getColumn(key).getType(); + String formattedValue = convertToSqlDataType(colType, indValue); + primaryKey = primaryKey + indValue; + rows.append(prefix).append(key).append(" = ").append(formattedValue); + prefix = " AND "; + } + + String using = getTTLSuffix(insObj); + String query = String.format("UPDATE %s%s SET %s WHERE %s;", tablename, using, + fields.toString(), rows.toString()); + LOG.debug(query); + + String consistency = extractConsistencyInfo(tablename, consistencyInfo); + executeCreateQuery(query, consistency); + } + + public void deleteRows(String tablename, Map<String, String> cols) { + String ns = ""; + String tbl = tablename; + int ix = tbl.indexOf('.'); + if (ix >= 0) { + ns = tablename.substring(0, ix); + tbl = tablename.substring(ix + 1); + } + Delete stmt = QueryBuilder.delete().from(ns, tbl); + if (cols.size() == 1) { + // only handles 1 WHERE value right now + String k = cols.keySet().iterator().next(); + Clause eqclause = QueryBuilder.eq(k, cols.get(k)); + session.execute(stmt.where(eqclause)); + } else { + session.execute(stmt); + } + } + + private String getTTLSuffix(JsonInsert insObj) { + String ttl = insObj.getTtl(); + String timestamp = insObj.getTimestamp(); + if (ttl != null && ttl.length() > 0) { + if (timestamp != null && timestamp.length() > 0) { + return " USING TTL " + ttl + " AND TIMESTAMP " + timestamp; + } else { + return " USING TTL " + ttl; + } + } else if (timestamp != null && timestamp.length() > 0) { + return " USING TIMESTAMP " + timestamp; + } + return ""; + } + + private MusicLockingService getLockingService() { + if (mls == null) { + mls = new MusicLockingService(music_hosts[0]); + } + return mls; + } + + private String extractConsistencyInfo(String key, Map<String, String> consistencyInfo) + throws Exception { + String consistency = ""; + if (consistencyInfo.get("type").equalsIgnoreCase("atomic")) { + String lockId = consistencyInfo.get("lockId"); + String lockName = lockId.substring(lockId.indexOf("$") + 1); + lockName = lockName.substring(0, lockName.indexOf("$")); + + // first ensure that the lock name is correct before seeing if it has access + if (!lockName.equalsIgnoreCase(key)) + throw new Exception("THIS LOCK IS NOT FOR THE KEY: " + key); + + String lockStatus = getLockingService().isMyTurn(lockId) + ""; + if (lockStatus.equalsIgnoreCase("false")) + throw new Exception("YOU DO NOT HAVE THE LOCK"); + return "atomic"; + } + if (consistencyInfo.get("type").equalsIgnoreCase("eventual")) + return "eventual"; + throw new Exception("Consistency type " + consistency + " unknown!!"); + } + + // utility function to parse json map into sql like string + private String jsonMaptoSqlString(Map<String, Object> jMap, String lineDelimiter) { + String sql = ""; + String prefix = ""; + for (Map.Entry<String, Object> entry : jMap.entrySet()) { + Object ot = entry.getValue(); + String value = ot + ""; + if (ot instanceof String) { + value = "'" + value + "'"; + } + sql = String.format("%s%s'%s': %s", sql, prefix, entry.getKey(), value); + prefix = lineDelimiter; + } + return sql; + } + + private String convertToSqlDataType(DataType type, Object valueObj) { + switch (type.getName()) { + case TEXT: + String t = valueObj.toString(); + t = t.replaceAll("'", "''"); + return "'" + t + "'"; + case MAP: + @SuppressWarnings("unchecked") + Map<String, Object> otMap = (Map<String, Object>) valueObj; + return "{" + jsonMaptoSqlString(otMap, ",") + "}"; + default: + case UUID: + return valueObj.toString(); + } + } + + private void executeCreateQuery(String query, String consistency) throws Exception { + Statement statement = new SimpleStatement(query); + if (consistency.equalsIgnoreCase("atomic")) + statement.setConsistencyLevel(ConsistencyLevel.ALL); + else if (consistency.equalsIgnoreCase("eventual")) + statement.setConsistencyLevel(ConsistencyLevel.ONE); + else + throw new Exception("Consistency level " + consistency + " unknown!!"); + session.execute(statement); + } +} diff --git a/src/main/java/org/onap/music/client/MusicRestClient.java b/src/main/java/org/onap/music/client/MusicRestClient.java new file mode 100644 index 00000000..93a2e12b --- /dev/null +++ b/src/main/java/org/onap/music/client/MusicRestClient.java @@ -0,0 +1,384 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.client; + +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import javax.ws.rs.core.MediaType; +import org.onap.music.datastore.jsonobjects.JsonDelete; +import org.onap.music.datastore.jsonobjects.JsonInsert; +import org.onap.music.datastore.jsonobjects.JsonKeySpace; +import org.onap.music.datastore.jsonobjects.JsonTable; +import org.onap.music.main.MusicUtil; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.api.client.config.ClientConfig; +import com.sun.jersey.api.client.config.DefaultClientConfig; +import com.sun.jersey.api.json.JSONConfiguration; + +public class MusicRestClient { + String[] musicNodes; + + public MusicRestClient(String[] musicNodes) { + this.musicNodes = musicNodes; + } + + public MusicRestClient(String oneMusicNode) { + musicNodes = new String[1]; + this.musicNodes[0] = oneMusicNode; + } + + public String getMusicNodeURL() { + String musicurl = "http://" + getMusicNodeIp() + ":8080/MUSIC/rest"; + return musicurl; + } + + private String getMusicNodeIp() { + Random r = new Random(); + int index = r.nextInt(musicNodes.length); + return musicNodes[index]; + } + + public void createKeyspace(String keyspaceName) { + Map<String, Object> replicationInfo = new HashMap<String, Object>(); + replicationInfo.put("class", "SimpleStrategy"); + replicationInfo.put("replication_factor", 1); + String durabilityOfWrites = "false"; + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "eventual"); + org.onap.music.datastore.jsonobjects.JsonKeySpace jsonKp = new JsonKeySpace(); + jsonKp.setConsistencyInfo(consistencyInfo); + jsonKp.setDurabilityOfWrites(durabilityOfWrites); + jsonKp.setReplicationInfo(replicationInfo); + + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + + WebResource webResource = client.resource(getMusicNodeURL() + "/keyspaces/" + keyspaceName); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .post(ClientResponse.class, jsonKp); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + } + + public void createStringMapTable(String keyspaceName, String tableName, + Map<String, String> fields) { + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "eventual"); + + JsonTable jtab = new JsonTable(); + jtab.setFields(fields); + jtab.setConsistencyInfo(consistencyInfo); + + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = getMusicNodeURL() + "/keyspaces/" + keyspaceName + "/tables/" + tableName; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .post(ClientResponse.class, jtab); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + } + + public void checkMusicVersion() { + Client client = Client.create(); + + WebResource webResource = client.resource(getMusicNodeURL() + "/version"); + + ClientResponse response = webResource.accept("text/plain").get(ClientResponse.class); + + if (response.getStatus() != 200) { + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + + String output = response.getEntity(String.class); + } + + public void createRow(String keyspaceName, String tableName, Map<String, Object> values) { + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "eventual"); + + JsonInsert jIns = new JsonInsert(); + jIns.setValues(values); + jIns.setConsistencyInfo(consistencyInfo); + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + + String url = getMusicNodeURL() + "/keyspaces/" + keyspaceName + "/tables/" + tableName + + "/rows"; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .post(ClientResponse.class, jIns); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus() + "url:" + + url + "values:" + values); + + + } + + private void basicUpdateRow(String keyspaceName, String tableName, String primaryKeyName, + String primaryKeyValue, Map<String, Object> values, + Map<String, String> consistencyInfo) { + JsonInsert jIns = new JsonInsert(); + jIns.setValues(values); + jIns.setConsistencyInfo(consistencyInfo); + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = getMusicNodeURL() + "/keyspaces/" + keyspaceName + "/tables/" + tableName + + "/rows?" + primaryKeyName + "=" + primaryKeyValue; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .put(ClientResponse.class, jIns); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus() + "url:" + + url + " values:" + values); + + } + + public void updateEntry(String keyspaceName, String tableName, String primaryKeyName, + String primaryKeyValue, Map<String, Object> values) { + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "eventual"); + basicUpdateRow(keyspaceName, tableName, primaryKeyName, primaryKeyValue, values, + consistencyInfo); + } + + public void deleteEntry(String keyspaceName, String tableName, String primaryKeyName, + String primaryKeyValue) { + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "eventual"); + + JsonDelete jDel = new JsonDelete(); + jDel.setConsistencyInfo(consistencyInfo); + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = getMusicNodeURL() + "/keyspaces/" + keyspaceName + "/tables/" + tableName + + "/rows?" + primaryKeyName + "=" + primaryKeyValue; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .delete(ClientResponse.class, jDel); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException( + "Failed : HTTP error code : " + response.getStatus() + "url:" + url); + + } + + public Map<String, Object> readRow(String keyspaceName, String tableName, String primaryKeyName, + String primaryKeyValue) { + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = getMusicNodeURL() + "/keyspaces/" + keyspaceName + "/tables/" + tableName + + "/rows?" + primaryKeyName + "=" + primaryKeyValue; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").get(ClientResponse.class); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + Map<String, Object> output = response.getEntity(Map.class); + return output; + } + + public Map<String, Object> readAllRows(String keyspaceName, String tableName) { + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = getMusicNodeURL() + "/keyspaces/" + keyspaceName + "/tables/" + tableName + + "/rows"; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").get(ClientResponse.class); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + Map<String, Object> output = response.getEntity(Map.class); + return output; + } + + + public void dropKeySpace(String keyspaceName) { + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "eventual"); + + JsonKeySpace jsonKp = new JsonKeySpace(); + jsonKp.setConsistencyInfo(consistencyInfo); + + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + + WebResource webResource = client.resource(getMusicNodeURL() + "/keyspaces/" + keyspaceName); + + ClientResponse response = + webResource.type("application/json").delete(ClientResponse.class, jsonKp); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + + public String createLock(String primaryKeyValue) { + Client client = Client.create(); + String msg = getMusicNodeURL() + "/locks/create/" + primaryKeyValue; + WebResource webResource = client.resource(msg); + + WebResource.Builder wb = webResource.accept(MediaType.TEXT_PLAIN); + + ClientResponse response = wb.post(ClientResponse.class); + + if (response.getStatus() != 200) { + throw new RuntimeException( + "Failed : HTTP error code : " + response.getStatus() + "url:" + msg); + } + + String output = response.getEntity(String.class); + + return output; + } + + public boolean acquireLock(String lockId) { + Client client = Client.create(); + String msg = getMusicNodeURL() + "/locks/acquire/" + lockId; + WebResource webResource = client.resource(msg); + + + WebResource.Builder wb = webResource.accept(MediaType.TEXT_PLAIN); + + ClientResponse response = wb.get(ClientResponse.class); + + if (response.getStatus() != 200) { + throw new RuntimeException( + "Failed : HTTP error code : " + response.getStatus() + "url:" + msg); + } + + String output = response.getEntity(String.class); + Boolean status = Boolean.parseBoolean(output); + return status; + } + + public void releaseLock(String lockId) { + Client client = Client.create(); + WebResource webResource = client.resource(getMusicNodeURL() + "/locks/release/" + lockId); + + ClientResponse response = webResource.delete(ClientResponse.class); + + + if (response.getStatus() != 204) { + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + } + + + public void updateRowAtomically(String keyspaceName, String tableName, String primaryKeyName, + String primaryKeyValue, Map<String, Object> values) { + /* + * create lock for the candidate. The music API dictates that the lock name must be of the + * form keyspacename.tableName.primaryKeyName + */ + + String lockName = keyspaceName + "." + tableName + "." + primaryKeyValue; + String lockId = createLock(lockName); + while (acquireLock(lockId) != true); + + + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "atomic"); + consistencyInfo.put("lockId", lockId); + + basicUpdateRow(keyspaceName, tableName, primaryKeyName, primaryKeyValue, values, + consistencyInfo); + + // release lock now that the operation is done + releaseLock(lockId); + + } + + public String getMusicId() { + + Client client = Client.create(); + + WebResource webResource = client.resource(getMusicNodeURL() + "/nodeId"); + ClientResponse response = webResource.accept("text/plain").get(ClientResponse.class); + + if (response.getStatus() != 200) { + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + + String output = response.getEntity(String.class); + return output; + } + + public void deleteRowAtomically(String keyspaceName, String tableName, String primaryKeyName, + String primaryKeyValue, Map<String, Object> values) { + + } + + public void deleteLock(String lockName) { + Client client = Client.create(); + WebResource webResource = client.resource(getMusicNodeURL() + "/locks/delete/" + lockName); + + ClientResponse response = webResource.delete(ClientResponse.class); + + + if (response.getStatus() != 204) { + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + } +} + diff --git a/src/main/java/org/onap/music/datastore/MusicDataStore.java b/src/main/java/org/onap/music/datastore/MusicDataStore.java new file mode 100644 index 00000000..c67c72e1 --- /dev/null +++ b/src/main/java/org/onap/music/datastore/MusicDataStore.java @@ -0,0 +1,391 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.datastore; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import org.onap.music.exceptions.MusicQueryException; +import org.onap.music.exceptions.MusicServiceException; +import org.onap.music.main.MusicUtil; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.ColumnDefinitions; +import com.datastax.driver.core.ColumnDefinitions.Definition; +import com.datastax.driver.core.ConsistencyLevel; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.KeyspaceMetadata; +import com.datastax.driver.core.Metadata; +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.TableMetadata; +import com.datastax.driver.core.exceptions.NoHostAvailableException; + +/** + * @author nelson24 + * + */ +public class MusicDataStore { + private Session session; + private Cluster cluster; + + + + /** + * @param session + */ + public void setSession(Session session) { + this.session = session; + } + + /** + * @param cluster + */ + public void setCluster(Cluster cluster) { + this.cluster = cluster; + } + + /** + * + */ + private static EELFLogger logger = EELFManager.getInstance().getLogger(MusicDataStore.class); + + /** + * + */ + public MusicDataStore() { + connectToCassaCluster(); + } + + + /** + * @param cluster + * @param session + */ + public MusicDataStore(Cluster cluster, Session session) { + this.session = session; + this.cluster = cluster; + } + + /** + * + * @param remoteIp + * @throws MusicServiceException + */ + public MusicDataStore(String remoteIp) { + try { + connectToCassaCluster(remoteIp); + } catch (MusicServiceException e) { + logger.error(e.getMessage()); + } + } + + /** + * + * @return + */ + private ArrayList<String> getAllPossibleLocalIps() { + ArrayList<String> allPossibleIps = new ArrayList<String>(); + try { + Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); + while (en.hasMoreElements()) { + NetworkInterface ni = (NetworkInterface) en.nextElement(); + Enumeration<InetAddress> ee = ni.getInetAddresses(); + while (ee.hasMoreElements()) { + InetAddress ia = (InetAddress) ee.nextElement(); + allPossibleIps.add(ia.getHostAddress()); + } + } + } catch (SocketException e) { + logger.error(e.getMessage()); + } + return allPossibleIps; + } + + /** + * This method iterates through all available IP addresses and connects to multiple cassandra + * clusters. + */ + private void connectToCassaCluster() { + Iterator<String> it = getAllPossibleLocalIps().iterator(); + String address = "localhost"; + logger.info("Connecting to cassa cluster: Iterating through possible ips:" + + getAllPossibleLocalIps()); + while (it.hasNext()) { + try { + cluster = Cluster.builder().withPort(9042) + .withCredentials(MusicUtil.getCassName(), MusicUtil.getCassPwd()) + .addContactPoint(address).build(); + Metadata metadata = cluster.getMetadata(); + logger.info("Connected to cassa cluster " + metadata.getClusterName() + " at " + + address); + session = cluster.connect(); + + break; + } catch (NoHostAvailableException e) { + address = it.next(); + logger.error(e.getMessage()); + } + } + } + + /** + * + */ + public void close() { + session.close(); + } + + /** + * This method connects to cassandra cluster on specific address. + * + * @param address + */ + private void connectToCassaCluster(String address) throws MusicServiceException { + cluster = Cluster.builder().withPort(9042) + .withCredentials(MusicUtil.getCassName(), MusicUtil.getCassPwd()) + .addContactPoint(address).build(); + Metadata metadata = cluster.getMetadata(); + logger.info("Connected to cassa cluster " + metadata.getClusterName() + " at " + address); + try { + session = cluster.connect(); + } catch (Exception ex) { + logger.error(ex.getMessage()); + throw new MusicServiceException( + "Error while connecting to Cassandra cluster.. " + ex.getMessage()); + } + } + + /** + * + * @param keyspace + * @param tableName + * @param columnName + * @return DataType + */ + public DataType returnColumnDataType(String keyspace, String tableName, String columnName) { + KeyspaceMetadata ks = cluster.getMetadata().getKeyspace(keyspace); + TableMetadata table = ks.getTable(tableName); + return table.getColumn(columnName).getType(); + + } + + /** + * + * @param keyspace + * @param tableName + * @return TableMetadata + */ + public TableMetadata returnColumnMetadata(String keyspace, String tableName) { + KeyspaceMetadata ks = cluster.getMetadata().getKeyspace(keyspace); + return ks.getTable(tableName); + } + + + /** + * Utility function to return the Java specific object type. + * + * @param row + * @param colName + * @param colType + * @return + */ + public Object getColValue(Row row, String colName, DataType colType) { + + switch (colType.getName()) { + case VARCHAR: + return row.getString(colName); + case UUID: + return row.getUUID(colName); + case VARINT: + return row.getVarint(colName); + case BIGINT: + return row.getLong(colName); + case INT: + return row.getInt(colName); + case FLOAT: + return row.getFloat(colName); + case DOUBLE: + return row.getDouble(colName); + case BOOLEAN: + return row.getBool(colName); + case MAP: + return row.getMap(colName, String.class, String.class); + default: + return null; + } + } + + public boolean doesRowSatisfyCondition(Row row, Map<String, Object> condition) { + ColumnDefinitions colInfo = row.getColumnDefinitions(); + + for (Map.Entry<String, Object> entry : condition.entrySet()) { + String colName = entry.getKey(); + DataType colType = colInfo.getType(colName); + Object columnValue = getColValue(row, colName, colType); + Object conditionValue = MusicUtil.convertToActualDataType(colType, entry.getValue()); + if (columnValue.equals(conditionValue) == false) + return false; + } + return true; + } + + /** + * Utility function to store ResultSet values in to a MAP for output. + * + * @param results + * @return MAP + */ + public Map<String, HashMap<String, Object>> marshalData(ResultSet results) { + Map<String, HashMap<String, Object>> resultMap = + new HashMap<String, HashMap<String, Object>>(); + int counter = 0; + for (Row row : results) { + ColumnDefinitions colInfo = row.getColumnDefinitions(); + HashMap<String, Object> resultOutput = new HashMap<String, Object>(); + for (Definition definition : colInfo) { + if (!definition.getName().equals("vector_ts")) + resultOutput.put(definition.getName(), + getColValue(row, definition.getName(), definition.getType())); + } + resultMap.put("row " + counter, resultOutput); + counter++; + } + return resultMap; + } + + + // Prepared Statements 1802 additions + /** + * This Method performs DDL and DML operations on Cassandra using specified consistency level + * + * @param queryObject Object containing cassandra prepared query and values. + * @param consistency Specify consistency level for data synchronization across cassandra + * replicas + * @return Boolean Indicates operation success or failure + * @throws MusicServiceException + * @throws MusicQueryException + */ + public boolean executePut(PreparedQueryObject queryObject, String consistency) + throws MusicServiceException, MusicQueryException { + + boolean result = false; + + if (!MusicUtil.isValidQueryObject(!queryObject.getValues().isEmpty(), queryObject)) { + logger.error("Error while processing prepared query object"); + throw new MusicQueryException("Ill formed queryObject for the request = " + "[" + + queryObject.getQuery() + "]"); + } + logger.info("In preprared Execute Put: the actual insert query:" + queryObject.getQuery() + + "; the values" + queryObject.getValues()); + PreparedStatement preparedInsert = session.prepare(queryObject.getQuery()); + try { + if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) { + logger.info("Executing critical put query"); + preparedInsert.setConsistencyLevel(ConsistencyLevel.QUORUM); + } else if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL)) { + logger.info("Executing simple put query"); + preparedInsert.setConsistencyLevel(ConsistencyLevel.ONE); + } + + session.execute(preparedInsert.bind(queryObject.getValues().toArray())); + result = true; + } catch (Exception e) { + logger.error("Executing Session Failure for Request = " + "[" + queryObject.getQuery() + + "]" + " Reason = " + e.getMessage()); + throw new MusicServiceException("Executing Session Failure for Request = " + "[" + + queryObject.getQuery() + "]" + " Reason = " + e.getMessage()); + } + + + return result; + } + + /** + * This method performs DDL operations on Cassandra using consistency level ONE. + * + * @param queryObject Object containing cassandra prepared query and values. + * @return ResultSet + * @throws MusicServiceException + * @throws MusicQueryException + */ + public ResultSet executeEventualGet(PreparedQueryObject queryObject) + throws MusicServiceException, MusicQueryException { + + if (!MusicUtil.isValidQueryObject(!queryObject.getValues().isEmpty(), queryObject)) { + throw new MusicQueryException("Ill formed queryObject for the request = " + "[" + + queryObject.getQuery() + "]"); + } + logger.info("Executing Eventual get query:" + queryObject.getQuery()); + PreparedStatement preparedEventualGet = session.prepare(queryObject.getQuery()); + preparedEventualGet.setConsistencyLevel(ConsistencyLevel.ONE); + ResultSet results = null; + try { + results = session.execute(preparedEventualGet.bind(queryObject.getValues().toArray())); + + } catch (Exception ex) { + logger.error(ex.getMessage()); + throw new MusicServiceException(ex.getMessage()); + } + return results; + } + + /** + * + * This method performs DDL operation on Cassandra using consistency level QUORUM. + * + * @param queryObject Object containing cassandra prepared query and values. + * @return ResultSet + * @throws MusicServiceException + * @throws MusicQueryException + */ + public ResultSet executeCriticalGet(PreparedQueryObject queryObject) + throws MusicServiceException, MusicQueryException { + if (!MusicUtil.isValidQueryObject(!queryObject.getValues().isEmpty(), queryObject)) { + logger.error("Error processing Prepared Query Object"); + throw new MusicQueryException("Ill formed queryObject for the request = " + "[" + + queryObject.getQuery() + "]"); + } + logger.info("Executing Critical get query:" + queryObject.getQuery()); + PreparedStatement preparedEventualGet = session.prepare(queryObject.getQuery()); + preparedEventualGet.setConsistencyLevel(ConsistencyLevel.QUORUM); + ResultSet results = null; + try { + results = session.execute(preparedEventualGet.bind(queryObject.getValues().toArray())); + } catch (Exception ex) { + logger.error(ex.getMessage()); + throw new MusicServiceException(ex.getMessage()); + } + return results; + + } + + +} diff --git a/src/main/java/org/onap/music/datastore/PreparedQueryObject.java b/src/main/java/org/onap/music/datastore/PreparedQueryObject.java new file mode 100644 index 00000000..694d9acd --- /dev/null +++ b/src/main/java/org/onap/music/datastore/PreparedQueryObject.java @@ -0,0 +1,79 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.datastore; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author srupane + * + */ +public class PreparedQueryObject { + + + private List<Object> values; + private StringBuilder query; + + + + /** + * + */ + public PreparedQueryObject() { + + this.values = new ArrayList<>(); + this.query = new StringBuilder(); + } + + /** + * @return + */ + public List<Object> getValues() { + return values; + } + + /** + * @param o + */ + public void addValue(Object o) { + this.values.add(o); + } + + /** + * @param s + */ + public void appendQueryString(String s) { + this.query.append(s); + } + + /** + * @return + */ + public String getQuery() { + return this.query.toString(); + } + + + +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/AAFResponse.java b/src/main/java/org/onap/music/datastore/jsonobjects/AAFResponse.java new file mode 100644 index 00000000..df6089ee --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/AAFResponse.java @@ -0,0 +1,42 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import java.util.ArrayList; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonTable", description = "Reponse class for AAF request") +public class AAFResponse { + + private ArrayList<NameSpace> ns = null; + + @ApiModelProperty(value = "Namespace value") + public ArrayList<NameSpace> getNs() { + return ns; + } + + public void setNs(ArrayList<NameSpace> ns) { + this.ns = ns; + } + +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/JsonDelete.java b/src/main/java/org/onap/music/datastore/jsonobjects/JsonDelete.java new file mode 100644 index 00000000..87c000b2 --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/JsonDelete.java @@ -0,0 +1,83 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import java.util.ArrayList; +import java.util.Map; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonTable", description = "Json model for delete") +public class JsonDelete { + + private ArrayList<String> columns = null; + private Map<String, String> consistencyInfo; + private Map<String, Object> conditions; + String ttl, timestamp; + + + @ApiModelProperty(value = "Conditions") + public Map<String, Object> getConditions() { + return conditions; + } + + public void setConditions(Map<String, Object> conditions) { + this.conditions = conditions; + } + + @ApiModelProperty(value = "Consistency level", allowableValues = "eventual,critical,atomic") + public Map<String, String> getConsistencyInfo() { + return consistencyInfo; + } + + public void setConsistencyInfo(Map<String, String> consistencyInfo) { + this.consistencyInfo = consistencyInfo; + } + + @ApiModelProperty(value = "Column values") + public ArrayList<String> getColumns() { + return columns; + } + + public void setColumns(ArrayList<String> columns) { + this.columns = columns; + } + + + @ApiModelProperty(value = "Time to live information") + public String getTtl() { + return ttl; + } + + public void setTtl(String ttl) { + this.ttl = ttl; + } + + @ApiModelProperty(value = "Time stamp") + public String getTimestamp() { + return timestamp; + } + + public void setTimestamp(String timestamp) { + this.timestamp = timestamp; + } +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/JsonInsert.java b/src/main/java/org/onap/music/datastore/jsonobjects/JsonInsert.java new file mode 100644 index 00000000..22c0104b --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/JsonInsert.java @@ -0,0 +1,118 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Map; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonTable", description = "Json model for table vlaues insert") +public class JsonInsert implements Serializable { + private String keyspaceName; + private String tableName; + private Map<String, Object> values; + String ttl, timestamp; + private Map<String, Object> row_specification; + private Map<String, String> consistencyInfo; + + @ApiModelProperty(value = "keyspace") + public String getKeyspaceName() { + return keyspaceName; + } + + public void setKeyspaceName(String keyspaceName) { + this.keyspaceName = keyspaceName; + } + + @ApiModelProperty(value = "Table name") + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + @ApiModelProperty(value = "Consistency level", allowableValues = "eventual,critical,atomic") + public Map<String, String> getConsistencyInfo() { + return consistencyInfo; + } + + public void setConsistencyInfo(Map<String, String> consistencyInfo) { + this.consistencyInfo = consistencyInfo; + } + + @ApiModelProperty(value = "Time to live information") + public String getTtl() { + return ttl; + } + + public void setTtl(String ttl) { + this.ttl = ttl; + } + + @ApiModelProperty(value = "Time stamp") + public String getTimestamp() { + return timestamp; + } + + public void setTimestamp(String timestamp) { + this.timestamp = timestamp; + } + + @ApiModelProperty(value = "values returned") + public Map<String, Object> getValues() { + return values; + } + + public void setValues(Map<String, Object> values) { + this.values = values; + } + + @ApiModelProperty(value = "Information for selecting specific rows for insert") + public Map<String, Object> getRow_specification() { + return row_specification; + } + + public void setRow_specification(Map<String, Object> row_specification) { + this.row_specification = row_specification; + } + + public byte[] serialize() { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = null; + try { + out = new ObjectOutputStream(bos); + out.writeObject(this); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return bos.toByteArray(); + } + +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/JsonKeySpace.java b/src/main/java/org/onap/music/datastore/jsonobjects/JsonKeySpace.java new file mode 100644 index 00000000..01b72672 --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/JsonKeySpace.java @@ -0,0 +1,73 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import java.util.Map; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonTable", description = "Json model creating new keyspace") +public class JsonKeySpace { + private String keyspaceName; + private Map<String, Object> replicationInfo; + private String durabilityOfWrites; + private Map<String, String> consistencyInfo; + + @ApiModelProperty(value = "Consistency level", allowableValues = "eventual,critical,atomic") + public Map<String, String> getConsistencyInfo() { + return consistencyInfo; + } + + public void setConsistencyInfo(Map<String, String> consistencyInfo) { + this.consistencyInfo = consistencyInfo; + } + + @ApiModelProperty(value = "Replication information") + public Map<String, Object> getReplicationInfo() { + return replicationInfo; + } + + public void setReplicationInfo(Map<String, Object> replicationInfo) { + this.replicationInfo = replicationInfo; + } + + @ApiModelProperty(value = "Durability", allowableValues = "true,false") + public String getDurabilityOfWrites() { + return durabilityOfWrites; + } + + public void setDurabilityOfWrites(String durabilityOfWrites) { + this.durabilityOfWrites = durabilityOfWrites; + } + + @ApiModelProperty(value = "Keyspace name") + public String getKeyspaceName() { + return keyspaceName; + } + + public void setKeyspaceName(String keyspaceName) { + this.keyspaceName = keyspaceName; + } + + + +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/JsonLeasedLock.java b/src/main/java/org/onap/music/datastore/jsonobjects/JsonLeasedLock.java new file mode 100644 index 00000000..85895baf --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/JsonLeasedLock.java @@ -0,0 +1,49 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonTable", description = "model for leased lock") +public class JsonLeasedLock { + long leasePeriod; + String notifyUrl; + + @ApiModelProperty(value = "Lease period") + public long getLeasePeriod() { + return leasePeriod; + } + + public void setLeasePeriod(long leasePeriod) { + this.leasePeriod = leasePeriod; + } + + @ApiModelProperty(value = "URL to be notified") + public String getNotifyUrl() { + return notifyUrl; + } + + public void setNotifyUrl(String notifyUrl) { + this.notifyUrl = notifyUrl; + } +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/JsonOnboard.java b/src/main/java/org/onap/music/datastore/jsonobjects/JsonOnboard.java new file mode 100755 index 00000000..11311733 --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/JsonOnboard.java @@ -0,0 +1,80 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonOnboard", description = "Defines the Json for Onboarding an application.") +public class JsonOnboard { + private String appname; + private String userId; + private String password; + private String isAAF; + private String aid; + + @ApiModelProperty(value = "Application Password") + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @ApiModelProperty(value = "Application UUID") + public String getAid() { + return aid; + } + + public void setAid(String aid) { + this.aid = aid; + } + + @ApiModelProperty(value = "Application name") + public String getAppname() { + return appname; + } + + public void setAppname(String appname) { + this.appname = appname; + } + + @ApiModelProperty(value = "User Id") + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + @ApiModelProperty(value = "Is AAF Application", allowableValues = "true, false") + public String getIsAAF() { + return isAAF; + } + + public void setIsAAF(String isAAF) { + this.isAAF = isAAF; + } + +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/JsonSelect.java b/src/main/java/org/onap/music/datastore/jsonobjects/JsonSelect.java new file mode 100644 index 00000000..0c2d012c --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/JsonSelect.java @@ -0,0 +1,56 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Map; + +public class JsonSelect implements Serializable { + private Map<String, String> consistencyInfo; + + + public Map<String, String> getConsistencyInfo() { + return consistencyInfo; + } + + public void setConsistencyInfo(Map<String, String> consistencyInfo) { + this.consistencyInfo = consistencyInfo; + } + + public byte[] serialize() { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = null; + try { + out = new ObjectOutputStream(bos); + out.writeObject(this); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return bos.toByteArray(); + } + +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/JsonTable.java b/src/main/java/org/onap/music/datastore/jsonobjects/JsonTable.java new file mode 100644 index 00000000..c0d8521d --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/JsonTable.java @@ -0,0 +1,113 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import java.util.Map; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonTable", description = "Defines the Json for Creating a new Table.") +public class JsonTable { + private String keyspaceName; + private String tableName; + + private Map<String, String> fields; + private Map<String, Object> properties; + private String primaryKey; + private String sortingKey; + private String clusteringOrder; + private Map<String, String> consistencyInfo; + + @ApiModelProperty(value = "Consistency level", allowableValues = "eventual,critical,atomic") + public Map<String, String> getConsistencyInfo() { + return consistencyInfo; + } + + public void setConsistencyInfo(Map<String, String> consistencyInfo) { + this.consistencyInfo = consistencyInfo; + } + + @ApiModelProperty(value = "Properties") + public Map<String, Object> getProperties() { + return properties; + } + + public void setProperties(Map<String, Object> properties) { + this.properties = properties; + } + + @ApiModelProperty(value = "Fields") + public Map<String, String> getFields() { + return fields; + } + + public void setFields(Map<String, String> fields) { + this.fields = fields; + } + + @ApiModelProperty(value = "KeySpace Name") + public String getKeyspaceName() { + return keyspaceName; + } + + public void setKeyspaceName(String keyspaceName) { + this.keyspaceName = keyspaceName; + } + + @ApiModelProperty(value = "Table Name") + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + @ApiModelProperty(value = "Sorting Key") + public String getSortingKey() { + return sortingKey; + } + + public void setSortingKey(String sortingKey) { + this.sortingKey = sortingKey; + } + + @ApiModelProperty(value = "Clustering Order", notes = "") + public String getClusteringOrder() { + return clusteringOrder; + } + + public void setClusteringOrder(String clusteringOrder) { + this.clusteringOrder = clusteringOrder; + } + + @ApiModelProperty(value = "Primary Key") + public String getPrimaryKey() { + return primaryKey; + } + + public void setPrimaryKey(String primaryKey) { + this.primaryKey = primaryKey; + } + + +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/JsonUpdate.java b/src/main/java/org/onap/music/datastore/jsonobjects/JsonUpdate.java new file mode 100644 index 00000000..fae7720d --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/JsonUpdate.java @@ -0,0 +1,128 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Map; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonTable", description = "Json model for table update") +public class JsonUpdate implements Serializable { + private String keyspaceName; + private String tableName; + private Map<String, Object> values; + private String ttl, timestamp; + private Map<String, String> consistencyInfo; + private Map<String, Object> conditions; + private Map<String, Object> row_specification; + + @ApiModelProperty(value = "Conditions") + public Map<String, Object> getConditions() { + return conditions; + } + + public void setConditions(Map<String, Object> conditions) { + this.conditions = conditions; + } + + @ApiModelProperty(value = "Information for selecting sepcific rows") + public Map<String, Object> getRow_specification() { + return row_specification; + } + + public void setRow_specification(Map<String, Object> row_specification) { + this.row_specification = row_specification; + } + + + @ApiModelProperty(value = "Keyspace name") + public String getKeyspaceName() { + return keyspaceName; + } + + public void setKeyspaceName(String keyspaceName) { + this.keyspaceName = keyspaceName; + } + + @ApiModelProperty(value = "Table name") + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + @ApiModelProperty(value = "Consistency level", allowableValues = "eventual,critical,atomic") + public Map<String, String> getConsistencyInfo() { + return consistencyInfo; + } + + public void setConsistencyInfo(Map<String, String> consistencyInfo) { + this.consistencyInfo = consistencyInfo; + } + + @ApiModelProperty(value = "Time to live value") + public String getTtl() { + return ttl; + } + + public void setTtl(String ttl) { + this.ttl = ttl; + } + + @ApiModelProperty(value = "Time stamp") + public String getTimestamp() { + return timestamp; + } + + public void setTimestamp(String timestamp) { + this.timestamp = timestamp; + } + + @ApiModelProperty(value = "Column values") + public Map<String, Object> getValues() { + return values; + } + + public void setValues(Map<String, Object> values) { + this.values = values; + } + + public byte[] serialize() { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = null; + try { + out = new ObjectOutputStream(bos); + out.writeObject(this); + } catch (IOException e) { + e.printStackTrace(); + } + return bos.toByteArray(); + } + +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/NameSpace.java b/src/main/java/org/onap/music/datastore/jsonobjects/NameSpace.java new file mode 100644 index 00000000..232353c1 --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/NameSpace.java @@ -0,0 +1,47 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import java.util.List; + + +public class NameSpace { + private String name; + private List<String> admin; + + public List<String> getAdmin() { + return admin; + } + + public String getName() { + return name; + } + + public void setAdmin(List<String> admin) { + this.admin = admin; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/src/main/java/org/onap/music/eelf/logging/EELFLoggerDelegate.java b/src/main/java/org/onap/music/eelf/logging/EELFLoggerDelegate.java new file mode 100644 index 00000000..27ae4712 --- /dev/null +++ b/src/main/java/org/onap/music/eelf/logging/EELFLoggerDelegate.java @@ -0,0 +1,354 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.eelf.logging; + +import static com.att.eelf.configuration.Configuration.MDC_SERVER_FQDN; +import static com.att.eelf.configuration.Configuration.MDC_SERVER_IP_ADDRESS; +import static com.att.eelf.configuration.Configuration.MDC_SERVICE_INSTANCE_ID; +import static com.att.eelf.configuration.Configuration.MDC_SERVICE_NAME; +import java.net.InetAddress; +import java.text.MessageFormat; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import javax.servlet.http.HttpServletRequest; +import org.slf4j.MDC; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.att.eelf.configuration.SLF4jWrapper; + +public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { + + public static final EELFLogger errorLogger = EELFManager.getInstance().getErrorLogger(); + public static final EELFLogger applicationLogger = + EELFManager.getInstance().getApplicationLogger(); + public static final EELFLogger auditLogger = EELFManager.getInstance().getAuditLogger(); + public static final EELFLogger metricsLogger = EELFManager.getInstance().getMetricsLogger(); + public static final EELFLogger debugLogger = EELFManager.getInstance().getDebugLogger(); + + private String className; + private static ConcurrentMap<String, EELFLoggerDelegate> classMap = new ConcurrentHashMap<>(); + + public EELFLoggerDelegate(final String className) { + super(className); + this.className = className; + } + + /** + * Convenience method that gets a logger for the specified class. + * + * @see #getLogger(String) + * + * @param clazz + * @return Instance of EELFLoggerDelegate + */ + public static EELFLoggerDelegate getLogger(Class<?> clazz) { + return getLogger(clazz.getName()); + } + + /** + * Gets a logger for the specified class name. If the logger does not already exist in the map, + * this creates a new logger. + * + * @param className If null or empty, uses EELFLoggerDelegate as the class name. + * @return Instance of EELFLoggerDelegate + */ + public static EELFLoggerDelegate getLogger(final String className) { + String classNameNeverNull = className == null || "".equals(className) + ? EELFLoggerDelegate.class.getName() + : className; + EELFLoggerDelegate delegate = classMap.get(classNameNeverNull); + if (delegate == null) { + delegate = new EELFLoggerDelegate(className); + classMap.put(className, delegate); + } + return delegate; + } + + /** + * Logs a message at the lowest level: trace. + * + * @param logger + * @param msg + */ + public void trace(EELFLogger logger, String msg) { + if (logger.isTraceEnabled()) { + logger.trace(msg); + } + } + + /** + * Logs a message with parameters at the lowest level: trace. + * + * @param logger + * @param msg + * @param arguments + */ + public void trace(EELFLogger logger, String msg, Object... arguments) { + if (logger.isTraceEnabled()) { + logger.trace(msg, arguments); + } + } + + /** + * Logs a message and throwable at the lowest level: trace. + * + * @param logger + * @param msg + * @param th + */ + public void trace(EELFLogger logger, String msg, Throwable th) { + if (logger.isTraceEnabled()) { + logger.trace(msg, th); + } + } + + /** + * Logs a message at the second-lowest level: debug. + * + * @param logger + * @param msg + */ + public void debug(EELFLogger logger, String msg) { + if (logger.isDebugEnabled()) { + logger.debug(msg); + } + } + + /** + * Logs a message with parameters at the second-lowest level: debug. + * + * @param logger + * @param msg + * @param arguments + */ + public void debug(EELFLogger logger, String msg, Object... arguments) { + if (logger.isDebugEnabled()) { + logger.debug(msg, arguments); + } + } + + /** + * Logs a message and throwable at the second-lowest level: debug. + * + * @param logger + * @param msg + * @param th + */ + public void debug(EELFLogger logger, String msg, Throwable th) { + if (logger.isDebugEnabled()) { + logger.debug(msg, th); + } + } + + /** + * Logs a message at info level. + * + * @param logger + * @param msg + */ + public void info(EELFLogger logger, String msg) { + logger.info(msg); + } + + /** + * Logs a message with parameters at info level. + * + * @param logger + * @param msg + * @param arguments + */ + public void info(EELFLogger logger, String msg, Object... arguments) { + logger.info(msg, arguments); + } + + /** + * Logs a message and throwable at info level. + * + * @param logger + * @param msg + * @param th + */ + public void info(EELFLogger logger, String msg, Throwable th) { + logger.info(msg, th); + } + + /** + * Logs a message at warn level. + * + * @param logger + * @param msg + */ + public void warn(EELFLogger logger, String msg) { + logger.warn(msg); + } + + /** + * Logs a message with parameters at warn level. + * + * @param logger + * @param msg + * @param arguments + */ + public void warn(EELFLogger logger, String msg, Object... arguments) { + logger.warn(msg, arguments); + } + + /** + * Logs a message and throwable at warn level. + * + * @param logger + * @param msg + * @param th + */ + public void warn(EELFLogger logger, String msg, Throwable th) { + logger.warn(msg, th); + } + + /** + * Logs a message at error level. + * + * @param logger + * @param msg + */ + public void error(EELFLogger logger, String msg) { + logger.error(msg); + } + + /** + * Logs a message with parameters at error level. + * + * @param logger + * @param msg + * @param arguments + */ + public void error(EELFLogger logger, String msg, Object... arguments) { + logger.warn(msg, arguments); + } + + /** + * Logs a message and throwable at error level. + * + * @param logger + * @param msg + * @param th + */ + public void error(EELFLogger logger, String msg, Throwable th) { + logger.warn(msg, th); + } + + /** + * Logs a message with the associated alarm severity at error level. + * + * @param logger + * @param msg + * @param severtiy + */ + public void error(EELFLogger logger, String msg, Object /* AlarmSeverityEnum */ severtiy) { + logger.error(msg); + } + + /** + * Initializes the logger context. + */ + public void init() { + setGlobalLoggingContext(); + final String msg = + "############################ Logging is started. ############################"; + // These loggers emit the current date-time without being told. + info(applicationLogger, msg); + error(errorLogger, msg); + debug(debugLogger, msg); + info(auditLogger, msg); + info(metricsLogger, msg); + } + + /** + * Builds a message using a template string and the arguments. + * + * @param message + * @param args + * @return + */ + private String formatMessage(String message, Object... args) { + StringBuilder sbFormattedMessage = new StringBuilder(); + if (args != null && args.length > 0 && message != null && message != "") { + MessageFormat mf = new MessageFormat(message); + sbFormattedMessage.append(mf.format(args)); + } else { + sbFormattedMessage.append(message); + } + + return sbFormattedMessage.toString(); + } + + /** + * Loads all the default logging fields into the MDC context. + */ + private void setGlobalLoggingContext() { + MDC.put(MDC_SERVICE_INSTANCE_ID, ""); + try { + MDC.put(MDC_SERVER_FQDN, InetAddress.getLocalHost().getHostName()); + MDC.put(MDC_SERVER_IP_ADDRESS, InetAddress.getLocalHost().getHostAddress()); + } catch (Exception e) { + errorLogger.error("setGlobalLoggingContext failed", e); + } + } + + public static void mdcPut(String key, String value) { + MDC.put(key, value); + } + + public static String mdcGet(String key) { + return MDC.get(key); + } + + public static void mdcRemove(String key) { + MDC.remove(key); + } + + /** + * Loads the RequestId/TransactionId into the MDC which it should be receiving with an each + * incoming REST API request. Also, configures few other request based logging fields into the + * MDC context. + * + * @param req + * @param appName + */ + public void setRequestBasedDefaultsIntoGlobalLoggingContext(HttpServletRequest req, + String appName) { + // Load the default fields + setGlobalLoggingContext(); + + // Load the request based fields + if (req != null) { + // Rest Path + MDC.put(MDC_SERVICE_NAME, req.getServletPath()); + + // Client IPAddress i.e. IPAddress of the remote host who is making + // this request. + String clientIPAddress = req.getHeader("X-FORWARDED-FOR"); + if (clientIPAddress == null) { + clientIPAddress = req.getRemoteAddr(); + } + } + } +} diff --git a/src/main/java/org/onap/music/exceptions/MusicLockingException.java b/src/main/java/org/onap/music/exceptions/MusicLockingException.java new file mode 100644 index 00000000..1a9e45d9 --- /dev/null +++ b/src/main/java/org/onap/music/exceptions/MusicLockingException.java @@ -0,0 +1,74 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.exceptions; + +/** + * @author inam + * + */ +public class MusicLockingException extends Exception { + + /** + * + */ + public MusicLockingException() { + + } + + /** + * @param message + */ + public MusicLockingException(String message) { + super(message); + + } + + /** + * @param cause + */ + public MusicLockingException(Throwable cause) { + super(cause); + + } + + /** + * @param message + * @param cause + */ + public MusicLockingException(String message, Throwable cause) { + super(message, cause); + + } + + /** + * @param message + * @param cause + * @param enableSuppression + * @param writableStackTrace + */ + public MusicLockingException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + + } + +} diff --git a/src/main/java/org/onap/music/exceptions/MusicPolicyVoilationException.java b/src/main/java/org/onap/music/exceptions/MusicPolicyVoilationException.java new file mode 100644 index 00000000..bade21a4 --- /dev/null +++ b/src/main/java/org/onap/music/exceptions/MusicPolicyVoilationException.java @@ -0,0 +1,79 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.exceptions; + +/** + * @author inam + * + */ +public class MusicPolicyVoilationException extends Exception { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * + */ + public MusicPolicyVoilationException() { + // TODO Auto-generated constructor stub + } + + /** + * @param message + */ + public MusicPolicyVoilationException(String message) { + super(message); + // TODO Auto-generated constructor stub + } + + /** + * @param cause + */ + public MusicPolicyVoilationException(Throwable cause) { + super(cause); + // TODO Auto-generated constructor stub + } + + /** + * @param message + * @param cause + */ + public MusicPolicyVoilationException(String message, Throwable cause) { + super(message, cause); + // TODO Auto-generated constructor stub + } + + /** + * @param message + * @param cause + * @param enableSuppression + * @param writableStackTrace + */ + public MusicPolicyVoilationException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + // TODO Auto-generated constructor stub + } + +} diff --git a/src/main/java/org/onap/music/exceptions/MusicQueryException.java b/src/main/java/org/onap/music/exceptions/MusicQueryException.java new file mode 100644 index 00000000..24b8568b --- /dev/null +++ b/src/main/java/org/onap/music/exceptions/MusicQueryException.java @@ -0,0 +1,89 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.exceptions; + + + +/** + * @author inam + * + */ +public class MusicQueryException extends Exception { + + /** + * + */ + private static final long serialVersionUID = 1L; + private int errorCode; + + + /** + * + */ + public MusicQueryException() { + super(); + } + + /** + * @param message + */ + public MusicQueryException(String message) { + super(message); + } + + + + /** + * @param message + */ + public MusicQueryException(String message, int errorCode) { + super(message); + this.errorCode = errorCode; + } + + /** + * @param cause + */ + public MusicQueryException(Throwable cause) { + super(cause); + } + + /** + * @param message + * @param cause + */ + public MusicQueryException(String message, Throwable cause) { + super(message, cause); + } + + /** + * @param message + * @param cause + * @param enableSuppression + * @param writableStackTrace + */ + public MusicQueryException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} diff --git a/src/main/java/org/onap/music/exceptions/MusicServiceException.java b/src/main/java/org/onap/music/exceptions/MusicServiceException.java new file mode 100644 index 00000000..a3b1fc56 --- /dev/null +++ b/src/main/java/org/onap/music/exceptions/MusicServiceException.java @@ -0,0 +1,84 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ + +package org.onap.music.exceptions; + +/** + * @author inam + * + */ +public class MusicServiceException extends Exception { + + + private int errorCode; + private String errorMessage; + + public int getErrorCode() { + return errorCode; + } + + + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + + + public String getErrorMessage() { + return errorMessage; + } + + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + + public MusicServiceException() { + super(); + } + + + public MusicServiceException(String message) { + super(message); + + } + + + public MusicServiceException(Throwable cause) { + super(cause); + + } + + + public MusicServiceException(String message, Throwable cause) { + super(message, cause); + + } + + + public MusicServiceException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + + } + +} diff --git a/src/main/java/org/onap/music/lockingservice/LockListener.java b/src/main/java/org/onap/music/lockingservice/LockListener.java new file mode 100644 index 00000000..33188e60 --- /dev/null +++ b/src/main/java/org/onap/music/lockingservice/LockListener.java @@ -0,0 +1,39 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.lockingservice; + +/** + * This class has two methods which are call back methods when a lock is acquired and when the lock + * is released. + * + */ +public interface LockListener { + /** + * call back called when the lock is acquired + */ + public void lockAcquired(); + + /** + * call back called when the lock is released. + */ + public void lockReleased(); +} diff --git a/src/main/java/org/onap/music/lockingservice/MusicLockState.java b/src/main/java/org/onap/music/lockingservice/MusicLockState.java new file mode 100644 index 00000000..23661ad4 --- /dev/null +++ b/src/main/java/org/onap/music/lockingservice/MusicLockState.java @@ -0,0 +1,126 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.lockingservice; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import org.onap.music.main.MusicCore; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +// the state variable that will be stored in zookeeper, capturing the transitions of +public class MusicLockState implements Serializable { + public enum LockStatus { + UNLOCKED, BEING_LOCKED, LOCKED + };// captures the state of the lock + + private static EELFLogger logger = EELFManager.getInstance().getLogger(MusicLockState.class); + LockStatus lockStatus; + boolean needToSyncQuorum = false; + String lockHolder; + long leasePeriod = Long.MAX_VALUE, leaseStartTime = -1; + + public MusicLockState(LockStatus lockStatus, String lockHolder) { + this.lockStatus = lockStatus; + this.lockHolder = lockHolder; + } + + public MusicLockState(LockStatus lockStatus, String lockHolder, boolean needToSyncQuorum) { + this.lockStatus = lockStatus; + this.lockHolder = lockHolder; + this.needToSyncQuorum = needToSyncQuorum; + } + + + public long getLeasePeriod() { + return leasePeriod; + } + + public boolean isNeedToSyncQuorum() { + return needToSyncQuorum; + } + + + + public void setLeasePeriod(long leasePeriod) { + this.leasePeriod = leasePeriod; + } + + + public long getLeaseStartTime() { + return leaseStartTime; + } + + + public void setLeaseStartTime(long leaseStartTime) { + this.leaseStartTime = leaseStartTime; + } + + + + public LockStatus getLockStatus() { + return lockStatus; + } + + public void setLockStatus(LockStatus lockStatus) { + this.lockStatus = lockStatus; + } + + public String getLockHolder() { + return lockHolder; + } + + public void setLockHolder(String lockHolder) { + this.lockHolder = lockHolder; + } + + public byte[] serialize() { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = null; + try { + out = new ObjectOutputStream(bos); + out.writeObject(this); + } catch (IOException e) { + logger.error(e.getMessage()); + } + return bos.toByteArray(); + } + + public static MusicLockState deSerialize(byte[] data) { + ByteArrayInputStream bis = new ByteArrayInputStream(data); + Object o = null; + ObjectInput in = null; + try { + in = new ObjectInputStream(bis); + o = in.readObject(); + } catch (ClassNotFoundException | IOException e) { + logger.error(e.getMessage()); + } + return (MusicLockState) o; + } +} diff --git a/src/main/java/org/onap/music/lockingservice/MusicLockingService.java b/src/main/java/org/onap/music/lockingservice/MusicLockingService.java new file mode 100644 index 00000000..59b502ca --- /dev/null +++ b/src/main/java/org/onap/music/lockingservice/MusicLockingService.java @@ -0,0 +1,142 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.lockingservice; + + +import java.io.IOException; +import java.util.StringTokenizer; +import java.util.concurrent.CountDownLatch; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.Watcher; +import org.apache.zookeeper.Watcher.Event.KeeperState; +import org.onap.music.exceptions.MusicServiceException; +import org.onap.music.main.MusicCore; +import org.onap.music.main.MusicUtil; +import org.apache.zookeeper.ZooKeeper; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.onap.music.datastore.MusicDataStore; +import org.onap.music.eelf.logging.EELFLoggerDelegate; + +public class MusicLockingService implements Watcher { + + private static final int SESSION_TIMEOUT = 180000; + ZkStatelessLockService zkLockHandle = null; + private CountDownLatch connectedSignal = new CountDownLatch(1); + private static EELFLogger logger = + EELFManager.getInstance().getLogger(MusicLockingService.class); + // private static EELFLoggerDelegate logger = + // EELFLoggerDelegate.getLogger(MusicLockingService.class); + + public MusicLockingService() throws MusicServiceException { + try { + ZooKeeper zk = new ZooKeeper(MusicUtil.getMyZkHost(), SESSION_TIMEOUT, this); + connectedSignal.await(); + zkLockHandle = new ZkStatelessLockService(zk); + } catch (IOException e) { + logger.error(e.getMessage()); + throw new MusicServiceException("IO Error has occured" + e.getMessage()); + } catch (InterruptedException e) { + logger.error(e.getMessage()); + throw new MusicServiceException("Exception Occured " + e.getMessage()); + } + } + + public ZkStatelessLockService getzkLockHandle() { + return zkLockHandle; + } + + public MusicLockingService(String lockServer) { + try { + ZooKeeper zk = new ZooKeeper(lockServer, SESSION_TIMEOUT, this); + connectedSignal.await(); + zkLockHandle = new ZkStatelessLockService(zk); + } catch (IOException | InterruptedException e) { + logger.error(e.getMessage()); + } + } + + public void createLockaIfItDoesNotExist(String lockName) { + if (zkLockHandle.checkIfLockExists(lockName) == false) { + String lockHolder = null; + MusicLockState ml = new MusicLockState(MusicLockState.LockStatus.UNLOCKED, lockHolder); + byte[] data = ml.serialize(); + zkLockHandle.createLock(lockName, data); + } + } + + public void setLockState(String lockName, MusicLockState mls) { + byte[] data = mls.serialize(); + zkLockHandle.setNodeData(lockName, data); + } + + public MusicLockState getLockState(String lockName) { + + byte[] data = zkLockHandle.getNodeData(lockName); + return MusicLockState.deSerialize(data); + } + + public String createLockId(String lockName) { + String lockIdWithSlash = zkLockHandle.createLockId(lockName); + return lockIdWithSlash.replace('/', '$'); + } + + public boolean isMyTurn(String lockIdWithDollar) { + String lockId = lockIdWithDollar.replace('$', '/'); + StringTokenizer st = new StringTokenizer(lockId); + String lockName = "/" + st.nextToken("/"); + try { + return zkLockHandle.lock(lockName, lockId); + } catch (KeeperException | InterruptedException e) { + logger.error(e.getMessage()); + } + return false; + } + + public void unlockAndDeleteId(String lockIdWithDollar) { + String lockId = lockIdWithDollar.replace('$', '/'); + zkLockHandle.unlock(lockId); + } + + public void deleteLock(String lockName) { + zkLockHandle.deleteLock(lockName); + } + + public String whoseTurnIsIt(String lockName) { + String lockHolder = zkLockHandle.currentLockHolder(lockName); + return lockHolder.replace('/', '$'); + + } + + public void process(WatchedEvent event) { // Watcher interface + if (event.getState() == KeeperState.SyncConnected) { + connectedSignal.countDown(); + } + } + + + public void close() { + zkLockHandle.close(); + } + +} diff --git a/src/main/java/org/onap/music/lockingservice/ProtocolSupport.java b/src/main/java/org/onap/music/lockingservice/ProtocolSupport.java new file mode 100644 index 00000000..37ae9e96 --- /dev/null +++ b/src/main/java/org/onap/music/lockingservice/ProtocolSupport.java @@ -0,0 +1,205 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.lockingservice; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.data.ACL; +import org.apache.zookeeper.data.Stat; +import org.onap.music.lockingservice.ZooKeeperOperation; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * A base class for protocol implementations which provides a number of higher level helper methods + * for working with ZooKeeper along with retrying synchronous operations if the connection to + * ZooKeeper closes such as {@link #retryOperation(ZooKeeperOperation)} + * + */ +class ProtocolSupport { + private static final Logger LOG = LoggerFactory.getLogger(ProtocolSupport.class); + + protected ZooKeeper zookeeper; + private AtomicBoolean closed = new AtomicBoolean(false); + private long retryDelay = 500L; + private int retryCount = 10; + private List<ACL> acl = ZooDefs.Ids.OPEN_ACL_UNSAFE; + + // public ProtocolSupport(ZooKeeper zookeeper) { + // this.zookeeper = zookeeper; + // } + + /** + * Closes this strategy and releases any ZooKeeper resources; but keeps the ZooKeeper instance + * open + */ + public void close() { + if (closed.compareAndSet(false, true)) { + doClose(); + } + } + + /** + * return zookeeper client instance + * + * @return zookeeper client instance + */ + public ZooKeeper getZookeeper() { + return zookeeper; + } + + /** + * return the acl its using + * + * @return the acl. + */ + public List<ACL> getAcl() { + return acl; + } + + /** + * set the acl + * + * @param acl the acl to set to + */ + public void setAcl(List<ACL> acl) { + this.acl = acl; + } + + /** + * get the retry delay in milliseconds + * + * @return the retry delay + */ + public long getRetryDelay() { + return retryDelay; + } + + /** + * Sets the time waited between retry delays + * + * @param retryDelay the retry delay + */ + public void setRetryDelay(long retryDelay) { + this.retryDelay = retryDelay; + } + + /** + * Allow derived classes to perform some custom closing operations to release resources + */ + protected void doClose() {} + + + /** + * Perform the given operation, retrying if the connection fails + * + * @return object. it needs to be cast to the callee's expected return type. + * @param operation FILL IN + * @throws KeeperException FILL IN + * @throws InterruptedException FILL IN + */ + protected Object retryOperation(ZooKeeperOperation operation) + throws KeeperException, InterruptedException { + KeeperException exception = null; + for (int i = 0; i < retryCount; i++) { + try { + return operation.execute(); + } catch (KeeperException.SessionExpiredException e) { + LOG.warn("Session expired for: " + zookeeper + " so reconnecting due to: " + e, e); + throw e; + } catch (KeeperException.ConnectionLossException e) { + if (exception == null) { + exception = e; + } + LOG.debug("Attempt " + i + " failed with connection loss so " + + "attempting to reconnect: " + e, e); + retryDelay(i); + } + } + throw exception; + } + + /** + * Ensures that the given path exists with no data, the current ACL and no flags + * + * @param path the lock path + */ + protected void ensurePathExists(String path) { + ensureExists(path, null, acl, CreateMode.PERSISTENT); + } + + /** + * Ensures that the given path exists with the given data, ACL and flags + * + * @param path the lock path + * @param data the data + * @param acl list of ACLs applying to the path + * @param flags create mode flags + */ + protected void ensureExists(final String path, final byte[] data, final List<ACL> acl, + final CreateMode flags) { + try { + retryOperation(new ZooKeeperOperation() { + public boolean execute() throws KeeperException, InterruptedException { + Stat stat = zookeeper.exists(path, false); + if (stat != null) { + return true; + } + zookeeper.create(path, data, acl, flags); + return true; + } + }); + } catch (KeeperException e) { + LOG.warn("Caught: " + e, e); + } catch (InterruptedException e) { + LOG.warn("Caught: " + e, e); + } + } + + /** + * Returns true if this protocol has been closed + * + * @return true if this protocol is closed + */ + protected boolean isClosed() { + return closed.get(); + } + + /** + * Performs a retry delay if this is not the first attempt + * + * @param attemptCount the number of the attempts performed so far + */ + protected void retryDelay(int attemptCount) { + if (attemptCount > 0) { + try { + Thread.sleep(attemptCount * retryDelay); + } catch (InterruptedException e) { + LOG.debug("Failed to sleep: " + e, e); + } + } + } +} diff --git a/src/main/java/org/onap/music/lockingservice/ZNodeName.java b/src/main/java/org/onap/music/lockingservice/ZNodeName.java new file mode 100644 index 00000000..c8d14cba --- /dev/null +++ b/src/main/java/org/onap/music/lockingservice/ZNodeName.java @@ -0,0 +1,117 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.lockingservice; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +/** + * Represents an ephemeral znode name which has an ordered sequence number and can be sorted in + * order + * + */ +class ZNodeName implements Comparable<ZNodeName> { + private final String name; + private String prefix; + private int sequence = -1; + private static EELFLogger LOG = EELFManager.getInstance().getLogger(ZNodeName.class); + + public ZNodeName(String name) { + if (name == null) { + throw new NullPointerException("id cannot be null"); + } + this.name = name; + this.prefix = name; + int idx = name.lastIndexOf('-'); + if (idx >= 0) { + this.prefix = name.substring(0, idx); + try { + this.sequence = Integer.parseInt(name.substring(idx + 1)); + // If an exception occurred we misdetected a sequence suffix, + // so return -1. + } catch (NumberFormatException e) { + LOG.info("Number format exception for " + idx, e); + } catch (ArrayIndexOutOfBoundsException e) { + LOG.info("Array out of bounds for " + idx, e); + } + } + } + + @Override + public String toString() { + return name.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + ZNodeName sequence = (ZNodeName) o; + + if (!name.equals(sequence.name)) + return false; + + return true; + } + + @Override + public int hashCode() { + return name.hashCode() + 37; + } + + public int compareTo(ZNodeName that) { + int answer = this.prefix.compareTo(that.prefix); + if (answer == 0) { + int s1 = this.sequence; + int s2 = that.sequence; + if (s1 == -1 && s2 == -1) { + return this.name.compareTo(that.name); + } + answer = s1 == -1 ? 1 : s2 == -1 ? -1 : s1 - s2; + } + return answer; + } + + /** + * Returns the name of the znode + */ + public String getName() { + return name; + } + + /** + * Returns the sequence number + */ + public int getZNodeName() { + return sequence; + } + + /** + * Returns the text prefix before the sequence number + */ + public String getPrefix() { + return prefix; + } +} diff --git a/src/main/java/org/onap/music/lockingservice/ZkStatelessLockService.java b/src/main/java/org/onap/music/lockingservice/ZkStatelessLockService.java new file mode 100644 index 00000000..99ccd4db --- /dev/null +++ b/src/main/java/org/onap/music/lockingservice/ZkStatelessLockService.java @@ -0,0 +1,334 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.lockingservice; + +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.data.ACL; +import org.apache.zookeeper.data.Stat; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +/** + * A <a href="package.html">protocol to implement an exclusive write lock or to elect a leader</a>. + * <p/> + * You invoke {@link #lock()} to start the process of grabbing the lock; you may get the lock then + * or it may be some time later. + * <p/> + * You can register a listener so that you are invoked when you get the lock; otherwise you can ask + * if you have the lock by calling {@link #isOwner()} + * + */ +public class ZkStatelessLockService extends ProtocolSupport { + public ZkStatelessLockService(ZooKeeper zk) { + zookeeper = zk; + } + + private static EELFLogger LOG = + EELFManager.getInstance().getLogger(ZkStatelessLockService.class); + + protected void createLock(final String path, final byte[] data) { + final List<ACL> acl = ZooDefs.Ids.OPEN_ACL_UNSAFE; + try { + retryOperation(new ZooKeeperOperation() { + public boolean execute() throws KeeperException, InterruptedException { + zookeeper.create(path, data, acl, CreateMode.PERSISTENT); + return true; + } + }); + } catch (KeeperException e) { + LOG.info("Caught: " + e, e); + } catch (InterruptedException e) { + LOG.info("Caught: " + e, e); + } + } + + public void close() { + try { + zookeeper.close(); + } catch (InterruptedException e) { + LOG.info(e.getMessage()); + } + } + + public void setNodeData(final String lockName, final byte[] data) { + try { + retryOperation(new ZooKeeperOperation() { + public boolean execute() throws KeeperException, InterruptedException { + zookeeper.getSessionId(); + zookeeper.setData("/" + lockName, data, -1); + return true; + } + }); + } catch (KeeperException e) { + LOG.info("Caught: " + e, e); + } catch (InterruptedException e) { + LOG.info("Caught: " + e, e); + } + + } + + public byte[] getNodeData(final String lockName) { + try { + if (zookeeper.exists("/" + lockName, null) != null) + return zookeeper.getData("/" + lockName, false, null); + else + return null; + + } catch (KeeperException e) { + LOG.info("Caught: " + e, e); + } catch (InterruptedException e) { + LOG.info("Caught: " + e, e); + } + return null; + } + + public boolean checkIfLockExists(String lockName) { + boolean result = false; + try { + Stat stat = zookeeper.exists(lockName, false); + if (stat != null) { + result = true; + } + } catch (KeeperException e) { + LOG.info(e.getMessage()); + } catch (InterruptedException e) { + LOG.info(e.getMessage()); + } + return result; + } + + public void createNode(String nodeName) { + ensurePathExists(nodeName); + } + + public String createLockId(String dir) { + ensurePathExists(dir); + LockZooKeeperOperation zop = new LockZooKeeperOperation(dir); + + try { + retryOperation(zop); + } catch (KeeperException e) { + LOG.info(e.getMessage()); + } catch (InterruptedException e) { + LOG.info(e.getMessage()); + } + return zop.getId(); + } + + /** + * Attempts to acquire the exclusive write lock returning whether or not it was acquired. Note + * that the exclusive lock may be acquired some time later after this method has been invoked + * due to the current lock owner going away. + */ + public synchronized boolean lock(String dir, String lockId) + throws KeeperException, InterruptedException { + if (isClosed()) { + return false; + } + LockZooKeeperOperation zop = new LockZooKeeperOperation(dir, lockId); + return (Boolean) retryOperation(zop); + } + + /** + * Removes the lock or associated znode if you no longer require the lock. this also removes + * your request in the queue for locking in case you do not already hold the lock. + * + * @throws RuntimeException throws a runtime exception if it cannot connect to zookeeper. + */ + public synchronized void unlock(String lockId) throws RuntimeException { + final String id = lockId; + if (!isClosed() && id != null) { + try { + ZooKeeperOperation zopdel = new ZooKeeperOperation() { + public boolean execute() throws KeeperException, InterruptedException { + zookeeper.delete(id, -1); + return Boolean.TRUE; + } + }; + zopdel.execute(); + } catch (InterruptedException e) { + LOG.info("Caught: " + e, e); + // set that we have been interrupted. + Thread.currentThread().interrupt(); + } catch (KeeperException.NoNodeException e) { + // do nothing + } catch (KeeperException e) { + LOG.info("Caught: " + e, e); + throw (RuntimeException) new RuntimeException(e.getMessage()).initCause(e); + } + } + } + + public synchronized String currentLockHolder(String mainLock) { + final String id = mainLock; + if (!isClosed() && id != null) { + List<String> names; + try { + names = zookeeper.getChildren(id, false); + if (names.isEmpty()) + return ""; + SortedSet<ZNodeName> sortedNames = new TreeSet<ZNodeName>(); + for (String name : names) { + sortedNames.add(new ZNodeName(id + "/" + name)); + } + return sortedNames.first().getName(); + } catch (InterruptedException e) { + LOG.info("Caught: " + e, e); + // set that we have been interrupted. + Thread.currentThread().interrupt(); + } catch (KeeperException.NoNodeException e) { + // do nothing + } catch (KeeperException e) { + LOG.info("Caught: " + e, e); + throw (RuntimeException) new RuntimeException(e.getMessage()).initCause(e); + } + } + return "No lock holder!"; + } + + public synchronized void deleteLock(String mainLock) { + final String id = mainLock; + if (!isClosed() && id != null) { + try { + ZooKeeperOperation zopdel = new ZooKeeperOperation() { + public boolean execute() throws KeeperException, InterruptedException { + List<String> names = zookeeper.getChildren(id, false); + for (String name : names) { + zookeeper.delete(id + "/" + name, -1); + } + zookeeper.delete(id, -1); + return Boolean.TRUE; + } + }; + zopdel.execute(); + } catch (InterruptedException e) { + LOG.info("Caught: " + e, e); + // set that we have been interrupted. + Thread.currentThread().interrupt(); + } catch (KeeperException.NoNodeException e) { + // do nothing + } catch (KeeperException e) { + LOG.info("Caught: " + e, e); + throw (RuntimeException) new RuntimeException(e.getMessage()).initCause(e); + } + } + + } + + /** + * a zoookeeper operation that is mainly responsible for all the magic required for locking. + */ + private class LockZooKeeperOperation implements ZooKeeperOperation { + + /** + * find if we have been created earlier if not create our node + * + * @param prefix the prefix node + * @param zookeeper the zookeeper client + * @param dir the dir parent + * @throws KeeperException + * @throws InterruptedException + */ + private String dir; + private String id = null; + + public String getId() { + return id; + } + + public LockZooKeeperOperation(String dir) { + this.dir = dir; + } + + public LockZooKeeperOperation(String dir, String id) { + this.dir = dir; + this.id = id; + } + + /** + * the command that is run and retried for actually obtaining the lock + * + * @return if the command was successful or not + */ + public boolean execute() throws KeeperException, InterruptedException { + do { + if (id == null) { + String prefix = "x-"; + byte[] data = {0x12, 0x34}; + id = zookeeper.create(dir + "/" + prefix, data, getAcl(), + CreateMode.PERSISTENT_SEQUENTIAL); + + if (LOG.isDebugEnabled()) { + LOG.debug("Created id: " + id); + } + if (id != null) + break; + } + if (id != null) { + List<String> names = zookeeper.getChildren(dir, false); + if (names.isEmpty()) { + LOG.info("No children in: " + dir + " when we've just " + + "created one! Lets recreate it..."); + // lets force the recreation of the id + id = null; + } else { + // lets sort them explicitly (though they do seem to come back in order + // ususally :) + ZNodeName idName = new ZNodeName(id); + SortedSet<ZNodeName> sortedNames = new TreeSet<ZNodeName>(); + for (String name : names) { + sortedNames.add(new ZNodeName(dir + "/" + name)); + } + if (!sortedNames.contains(idName)) + return Boolean.FALSE; + + SortedSet<ZNodeName> lessThanMe = sortedNames.headSet(idName); + if (!lessThanMe.isEmpty()) { + ZNodeName lastChildName = lessThanMe.last(); + String lastChildId = lastChildName.getName(); + if (LOG.isDebugEnabled()) { + LOG.debug("watching less than me node: " + lastChildId); + } + Stat stat = zookeeper.exists(lastChildId, false); + if (stat != null) { + return Boolean.FALSE; + } else { + LOG.info("Could not find the" + " stats for less than me: " + + lastChildName.getName()); + } + } else + return Boolean.TRUE; + } + } + } while (id == null); + return Boolean.FALSE; + } + } + +} + diff --git a/src/main/java/org/onap/music/lockingservice/ZooKeeperOperation.java b/src/main/java/org/onap/music/lockingservice/ZooKeeperOperation.java new file mode 100644 index 00000000..7020d14d --- /dev/null +++ b/src/main/java/org/onap/music/lockingservice/ZooKeeperOperation.java @@ -0,0 +1,42 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.lockingservice; + +import org.apache.zookeeper.KeeperException; + +/** + * A callback object which can be used for implementing retry-able operations in the + * {@link org.onap.music.lockingservice.ProtocolSupport} class + * + */ +public interface ZooKeeperOperation { + + /** + * Performs the operation - which may be involved multiple times if the connection + * to ZooKeeper closes during this operation + * + * @return the result of the operation or null + * @throws KeeperException FILL IN + * @throws InterruptedException FILL IN + */ + public boolean execute() throws KeeperException, InterruptedException; +} diff --git a/src/main/java/org/onap/music/main/CachingUtil.java b/src/main/java/org/onap/music/main/CachingUtil.java new file mode 100755 index 00000000..0ab055df --- /dev/null +++ b/src/main/java/org/onap/music/main/CachingUtil.java @@ -0,0 +1,370 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.main; + +import java.util.Arrays; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.jcs.JCS; +import org.apache.commons.jcs.access.CacheAccess; +import org.apache.log4j.Logger; +import org.codehaus.jackson.map.ObjectMapper; +import org.onap.music.datastore.jsonobjects.AAFResponse; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.onap.music.datastore.PreparedQueryObject; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.WebResource; + +/** + * All Caching related logic is handled by this class and a schedule cron runs to update cache. + * + * @author Vikram + * + */ +public class CachingUtil implements Runnable { + + private static EELFLogger logger = EELFManager.getInstance().getLogger(CachingUtil.class); + + private static CacheAccess<String, String> musicCache = JCS.getInstance("musicCache"); + private static CacheAccess<String, Map<String, String>> aafCache = JCS.getInstance("aafCache"); + private static CacheAccess<String, String> appNameCache = JCS.getInstance("appNameCache"); + private static Map<String, Number> userAttempts = new HashMap<>(); + private static Map<String, Calendar> lastFailedTime = new HashMap<>(); + + public boolean isCacheRefreshNeeded() { + if (aafCache.get("initBlankMap") == null) + return true; + return false; + } + + public void initializeMusicCache() { + logger.info("Initializing Music Cache..."); + musicCache.put("isInitialized", "true"); + } + + public void initializeAafCache() { + logger.info("Resetting and initializing AAF Cache..."); + + // aafCache.clear(); + // loop through aafCache ns .. only the authenticated ns will be re cached. and non + // authenticated will wait for user to retry. + String query = "SELECT application_name, keyspace_name, username, password FROM admin.keyspace_master WHERE is_api = ? allow filtering"; + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString(query); + try { + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), false)); + } catch (Exception e1) { + e1.printStackTrace(); + logger.error("Exception is " + e1.getMessage() + "during initalizeAafCache"); + } + ResultSet rs = MusicCore.get(pQuery); + Iterator<Row> it = rs.iterator(); + Map<String, String> map = null; + while (it.hasNext()) { + Row row = it.next(); + String nameSpace = row.getString("keyspace_name"); + String userId = row.getString("username"); + String password = row.getString("password"); + String keySpace = row.getString("application_name"); + try { + userAttempts.put(nameSpace, 0); + AAFResponse responseObj = triggerAAF(nameSpace, userId, password); + if (responseObj.getNs().size() > 0) { + map = new HashMap<>(); + map.put(userId, password); + aafCache.put(nameSpace, map); + musicCache.put(nameSpace, keySpace); + logger.debug("Cronjob: Cache Updated with AAF response for namespace " + + nameSpace); + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + logger.error("Something at AAF was changed for ns: " + nameSpace + + ". So not updating Cache for the namespace. "); + logger.error("Exception is " + e.getMessage()); + } + } + + } + + @Override + public void run() { + logger.debug("Scheduled task invoked. Refreshing Cache..."); + initializeAafCache(); + } + + public static boolean authenticateAAFUser(String nameSpace, String userId, String password, + String keySpace) throws Exception { + + if (aafCache.get(nameSpace) != null) { + if (!musicCache.get(nameSpace).equals(keySpace)) { + logger.debug("Create new application for the same namespace."); + } else if (aafCache.get(nameSpace).get(userId).equals(password)) { + logger.debug("Authenticated with cache value.."); + // reset invalid attempts to 0 + userAttempts.put(nameSpace, 0); + return true; + } else { + // call AAF update cache with new password + if (userAttempts.get(nameSpace) == null) + userAttempts.put(nameSpace, 0); + if ((Integer) userAttempts.get(nameSpace) >= 3) { + logger.info("Reached max attempts. Checking if time out.."); + logger.info("Failed time: " + lastFailedTime.get(nameSpace).getTime()); + Calendar calendar = Calendar.getInstance(); + long delayTime = (calendar.getTimeInMillis() + - lastFailedTime.get(nameSpace).getTimeInMillis()); + logger.info("Delayed time: " + delayTime); + if (delayTime > 120000) { + logger.info("Resetting failed attempt."); + userAttempts.put(nameSpace, 0); + } else { + throw new Exception( + "No more attempts allowed. Please wait for atleast 2 min."); + } + } + logger.error("Cache not authenticated.."); + logger.info("Check AAF again..."); + } + } + + AAFResponse responseObj = triggerAAF(nameSpace, userId, password); + if (responseObj.getNs().size() > 0) { + if (responseObj.getNs().get(0).getAdmin().contains(userId)) + return true; + + } + logger.info("Invalid user. Cache not updated"); + return false; + } + + private static AAFResponse triggerAAF(String nameSpace, String userId, String password) + throws Exception { + if (MusicUtil.getAafEndpointUrl() == null) { + throw new Exception("AAF endpoint is not set. Please specify in the properties file."); + } + Client client = Client.create(); + // WebResource webResource = + // client.resource("https://aaftest.test.att.com:8095/proxy/authz/nss/"+nameSpace); + WebResource webResource = client.resource(MusicUtil.getAafEndpointUrl().concat(nameSpace)); + String plainCreds = userId + ":" + password; + byte[] plainCredsBytes = plainCreds.getBytes(); + byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes); + String base64Creds = new String(base64CredsBytes); + + ClientResponse response = webResource.accept(MediaType.APPLICATION_JSON) + .header("Authorization", "Basic " + base64Creds) + .header("content-type", "application/json").get(ClientResponse.class); + if (response.getStatus() != 200) { + if (userAttempts.get(nameSpace) == null) + userAttempts.put(nameSpace, 0); + if ((Integer) userAttempts.get(nameSpace) >= 2) { + lastFailedTime.put(nameSpace, Calendar.getInstance()); + userAttempts.put(nameSpace, ((Integer) userAttempts.get(nameSpace) + 1)); + throw new Exception( + "Reached max invalid attempts. Please contact admin and retry with valid credentials."); + } + userAttempts.put(nameSpace, ((Integer) userAttempts.get(nameSpace) + 1)); + throw new Exception( + "Unable to authenticate. Please check the AAF credentials against namespace."); + // TODO Allow for 2-3 times and forbid any attempt to trigger AAF with invalid values + // for specific time. + } + response.getHeaders().put(HttpHeaders.CONTENT_TYPE, + Arrays.asList(MediaType.APPLICATION_JSON)); + // AAFResponse output = response.getEntity(AAFResponse.class); + response.bufferEntity(); + String x = response.getEntity(String.class); + AAFResponse responseObj = new ObjectMapper().readValue(x, AAFResponse.class); + return responseObj; + } + + public static Map<String, Object> authenticateAIDUser(String aid, String keyspace) + throws Exception { + Map<String, Object> resultMap = new HashMap<>(); + String uuid = null; + /* + * if(aid == null || aid.length() == 0) { resultMap.put("Exception Message", + * "AID is missing for the keyspace requested."); //create a new AID ?? } else + */ + if (musicCache.get(keyspace) == null) { + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "SELECT uuid from admin.keyspace_master where keyspace_name = '" + + keyspace + "' allow filtering"); + Row rs = MusicCore.get(pQuery).one(); + try { + uuid = rs.getUUID("uuid").toString(); + musicCache.put(keyspace, uuid); + } catch (Exception e) { + String msg = e.getMessage(); + logger.error("Exception occured during uuid retrieval from DB." + e.getMessage()); + resultMap.put("Exception", "Unauthorized operation. Check AID and Keyspace. " + + "Exception from MUSIC is: " + + (msg == null ? "Keyspace is new so no AID should be passed in Header." + : msg)); + return resultMap; + } + if (!musicCache.get(keyspace).toString().equals(aid)) { + resultMap.put("Exception Message", + "Unauthorized operation. Invalid AID for the keyspace"); + return resultMap; + } + } else if (musicCache.get(keyspace) != null + && !musicCache.get(keyspace).toString().equals(aid)) { + resultMap.put("Exception Message", + "Unauthorized operation. Invalid AID for the keyspace"); + return resultMap; + } + resultMap.put("aid", uuid); + return resultMap; + } + + public static void updateMusicCache(String aid, String keyspace) { + logger.info("Updating musicCache for keyspace " + keyspace + " with aid " + aid); + musicCache.put(keyspace, aid); + } + + public static void updateisAAFCache(String namespace, String isAAF) { + appNameCache.put(namespace, isAAF); + } + + public static Boolean isAAFApplication(String namespace) { + + String isAAF = appNameCache.get(namespace); + if (isAAF == null) { + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "SELECT is_aaf from admin.keyspace_master where application_name = '" + + namespace + "' allow filtering"); + Row rs = MusicCore.get(pQuery).one(); + try { + isAAF = String.valueOf(rs.getBool("is_aaf")); + appNameCache.put(namespace, isAAF); + } catch (Exception e) { + logger.error("Exception occured during uuid retrieval from DB." + e.getMessage()); + e.printStackTrace(); + } + } + return Boolean.valueOf(isAAF); + } + + public static String getUuidFromMusicCache(String keyspace) { + String uuid = musicCache.get(keyspace); + if (uuid == null) { + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "SELECT uuid from admin.keyspace_master where keyspace_name = '" + + keyspace + "' allow filtering"); + Row rs = MusicCore.get(pQuery).one(); + try { + uuid = rs.getUUID("uuid").toString(); + musicCache.put(keyspace, uuid); + } catch (Exception e) { + logger.error("Exception occured during uuid retrieval from DB." + e.getMessage()); + e.printStackTrace(); + } + } + return uuid; + } + + public static String getAppName(String keyspace) { + String appName = null; + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "SELECT application_name from admin.keyspace_master where keyspace_name = '" + + keyspace + "' allow filtering"); + Row rs = MusicCore.get(pQuery).one(); + try { + appName = rs.getString("application_name"); + } catch (Exception e) { + logger.error("Exception occured during uuid retrieval from DB." + e.getMessage()); + e.printStackTrace(); + } + return appName; + } + + public static String generateUUID() { + String uuid = UUID.randomUUID().toString(); + logger.info("New AID generated: " + uuid); + return uuid; + } + + public static Map<String, Object> validateRequest(String nameSpace, String userId, + String password, String keyspace, String aid, String operation) { + Map<String, Object> resultMap = new HashMap<>(); + if (!"createKeySpace".equals(operation)) { + if (nameSpace == null) { + resultMap.put("Exception", "Application namespace is mandatory."); + } + } + return resultMap; + + } + + public static Map<String, Object> verifyOnboarding(String ns, String userId, String password) + throws Exception { + Map<String, Object> resultMap = new HashMap<>(); + if (ns == null || userId == null || password == null) { + logger.error("One or more required headers is missing. userId: " + userId + + " :: password: " + password); + resultMap.put("Exception", + "One or more required headers appName(ns), userId, password is missing. Please check."); + return resultMap; + } + PreparedQueryObject queryObject = new PreparedQueryObject(); + queryObject.appendQueryString( + "select * from admin.keyspace_master where application_name=? and username=? allow filtering"); + queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), ns)); + queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), userId)); + Row rs = MusicCore.get(queryObject).one(); + if (rs == null) { + logger.error("Namespace and UserId doesn't match. namespace: " + ns + " and userId: " + + userId); + resultMap.put("Exception", "Application " + ns + + " doesn't seem to be Onboarded. Please onboard your application with MUSIC. If already onboarded contact Admin"); + } else { + boolean is_aaf = rs.getBool("is_aaf"); + String keyspace = rs.getString("keyspace_name"); + if (!is_aaf) { + if (!keyspace.equals(MusicUtil.DEFAULTKEYSPACENAME)) { + logger.error("Non AAF applications are allowed to have only one keyspace per application."); + resultMap.put("Exception", + "Non AAF applications are allowed to have only one keyspace per application."); + } + } + } + return resultMap; + } +} diff --git a/src/main/java/org/onap/music/main/CronJobManager.java b/src/main/java/org/onap/music/main/CronJobManager.java new file mode 100644 index 00000000..fb4a2ac3 --- /dev/null +++ b/src/main/java/org/onap/music/main/CronJobManager.java @@ -0,0 +1,47 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.main; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.annotation.WebListener; + +@WebListener +public class CronJobManager implements ServletContextListener { + + private ScheduledExecutorService scheduler; + + @Override + public void contextInitialized(ServletContextEvent event) { + scheduler = Executors.newSingleThreadScheduledExecutor(); + scheduler.scheduleAtFixedRate(new CachingUtil(), 0, 24, TimeUnit.HOURS); + } + + @Override + public void contextDestroyed(ServletContextEvent event) { + scheduler.shutdownNow(); + } + +} diff --git a/src/main/java/org/onap/music/main/MusicCore.java b/src/main/java/org/onap/music/main/MusicCore.java new file mode 100644 index 00000000..592bae92 --- /dev/null +++ b/src/main/java/org/onap/music/main/MusicCore.java @@ -0,0 +1,874 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.main; + + +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; +import org.onap.music.datastore.MusicDataStore; +import org.onap.music.datastore.PreparedQueryObject; +import org.onap.music.datastore.jsonobjects.JsonKeySpace; +// import org.onap.music.eelf.logging.EELFLoggerDelegate; +import org.onap.music.exceptions.MusicLockingException; +import org.onap.music.exceptions.MusicQueryException; +import org.onap.music.exceptions.MusicServiceException; +import org.onap.music.lockingservice.MusicLockState; +import org.onap.music.lockingservice.MusicLockState.LockStatus; +import org.onap.music.lockingservice.MusicLockingService; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.datastax.driver.core.ColumnDefinitions; +import com.datastax.driver.core.ColumnDefinitions.Definition; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.TableMetadata; + +/** + * This class ..... + * + * + */ +public class MusicCore { + + public static MusicLockingService mLockHandle = null; + public static MusicDataStore mDstoreHandle = null; + private static EELFLogger logger = EELFManager.getInstance().getLogger(MusicCore.class); + + public static class Condition { + Map<String, Object> conditions; + String selectQueryForTheRow; + + public Condition(Map<String, Object> conditions, String selectQueryForTheRow) { + this.conditions = conditions; + this.selectQueryForTheRow = selectQueryForTheRow; + } + + public boolean testCondition() { + // first generate the row + PreparedQueryObject query = new PreparedQueryObject(); + query.appendQueryString(selectQueryForTheRow); + ResultSet results = quorumGet(query); + Row row = results.one(); + return getDSHandle().doesRowSatisfyCondition(row, conditions); + } + } + + + public static MusicLockingService getLockingServiceHandle() throws MusicLockingException { + logger.info("Acquiring lock store handle"); + long start = System.currentTimeMillis(); + + if (mLockHandle == null) { + try { + mLockHandle = new MusicLockingService(); + } catch (Exception e) { + logger.error("Failed to aquire Locl store handle" + e.getMessage()); + throw new MusicLockingException("Failed to aquire Locl store handle " + e); + } + } + long end = System.currentTimeMillis(); + logger.info("Time taken to acquire lock store handle:" + (end - start) + " ms"); + return mLockHandle; + } + + /** + * + * @param remoteIp + * @return + */ + public static MusicDataStore getDSHandle(String remoteIp) { + logger.info("Acquiring data store handle"); + long start = System.currentTimeMillis(); + if (mDstoreHandle == null) { + mDstoreHandle = new MusicDataStore(remoteIp); + } + long end = System.currentTimeMillis(); + logger.info("Time taken to acquire data store handle:" + (end - start) + " ms"); + return mDstoreHandle; + } + + /** + * + * @return + */ + public static MusicDataStore getDSHandle() { + logger.info("Acquiring data store handle"); + long start = System.currentTimeMillis(); + if (mDstoreHandle == null) { + mDstoreHandle = new MusicDataStore(); + } + long end = System.currentTimeMillis(); + logger.info("Time taken to acquire data store handle:" + (end - start) + " ms"); + return mDstoreHandle; + } + + public static String createLockReference(String lockName) { + logger.info("Creating lock reference for lock name:" + lockName); + long start = System.currentTimeMillis(); + String lockId = null; + try { + lockId = getLockingServiceHandle().createLockId("/" + lockName); + } catch (MusicLockingException e) { + logger.error("Failed to create Lock Reference " + lockName); + } + long end = System.currentTimeMillis(); + logger.info("Time taken to create lock reference:" + (end - start) + " ms"); + return lockId; + } + + /** + * + * @param key + * @return + */ + public static boolean isTableOrKeySpaceLock(String key) { + String[] splitString = key.split("\\."); + if (splitString.length > 2) + return false; + else + return true; + } + + /** + * + * @param key + * @return + */ + public static MusicLockState getMusicLockState(String key) { + long start = System.currentTimeMillis(); + try { + String[] splitString = key.split("\\."); + String keyspaceName = splitString[0]; + String tableName = splitString[1]; + String primaryKey = splitString[2]; + MusicLockState mls; + String lockName = keyspaceName + "." + tableName + "." + primaryKey; + mls = getLockingServiceHandle().getLockState(lockName); + long end = System.currentTimeMillis(); + logger.info("Time taken to get lock state:" + (end - start) + " ms"); + return mls; + } catch (NullPointerException | MusicLockingException e) { + logger.error("No lock object exists as of now.." + e); + } + return null; + } + + public static ReturnType acquireLockWithLease(String key, String lockId, long leasePeriod) { + try { + long start = System.currentTimeMillis(); + /* check if the current lock has exceeded its lease and if yes, release that lock */ + MusicLockState mls = getMusicLockState(key); + if (mls != null) { + if (mls.getLockStatus().equals(LockStatus.LOCKED)) { + logger.info("The current lock holder for " + key + " is " + mls.getLockHolder() + + ". Checking if it has exceeded lease"); + long currentLockPeriod = System.currentTimeMillis() - mls.getLeaseStartTime(); + long currentLeasePeriod = mls.getLeasePeriod(); + if (currentLockPeriod > currentLeasePeriod) { + logger.info("Lock period " + currentLockPeriod + + " has exceeded lease period " + currentLeasePeriod); + boolean voluntaryRelease = false; + String currentLockHolder = mls.getLockHolder(); + mls = releaseLock(currentLockHolder, voluntaryRelease); + } + } + } else + logger.debug("There is no lock state object for " + key); + + /* + * call the traditional acquire lock now and if the result returned is true, set the + * begin time-stamp and lease period + */ + if (acquireLock(key, lockId) == true) { + mls = getMusicLockState(key);// get latest state + if (mls.getLeaseStartTime() == -1) {// set it again only if it is not set already + mls.setLeaseStartTime(System.currentTimeMillis()); + mls.setLeasePeriod(leasePeriod); + getLockingServiceHandle().setLockState(key, mls); + } + long end = System.currentTimeMillis(); + logger.info("Time taken to acquire leased lock:" + (end - start) + " ms"); + return new ReturnType(ResultType.SUCCESS, "Accquired lock"); + } else { + long end = System.currentTimeMillis(); + logger.info("Time taken to fail to acquire leased lock:" + (end - start) + " ms"); + return new ReturnType(ResultType.FAILURE, "Could not acquire lock"); + } + } catch (Exception e) { + StringWriter sw = new StringWriter(); + logger.error(e.getMessage()); + String exceptionAsString = sw.toString(); + return new ReturnType(ResultType.FAILURE, + "Exception thrown in acquireLockWithLease:\n" + exceptionAsString); + } + } + + public static boolean acquireLock(String key, String lockId) { + /* + * first check if I am on top. Since ids are not reusable there is no need to check + * lockStatus If the status is unlocked, then the above call will automatically return + * false. + */ + Boolean result = false; + try { + result = getLockingServiceHandle().isMyTurn(lockId); + } catch (MusicLockingException e2) { + logger.error("Failed to aquireLock lockId " + lockId + " " + e2); + } + if (result == false) { + logger.info("In acquire lock: Not your turn, someone else has the lock"); + return false; + } + + + // this is for backward compatibility where locks could also be acquired on just + // keyspaces or tables. + if (isTableOrKeySpaceLock(key) == true) { + logger.info("In acquire lock: A table or keyspace lock so no need to perform sync...so returning true"); + return true; + } + + // read the lock name corresponding to the key and if the status is locked or being locked, + // then return false + MusicLockState currentMls = null; + MusicLockState newMls = null; + try { + currentMls = getMusicLockState(key); + String currentLockHolder = currentMls.getLockHolder(); + if (lockId.equals(currentLockHolder)) { + logger.info("In acquire lock: You already have the lock!"); + return true; + } + } catch (NullPointerException e) { + logger.error("In acquire lock:No one has tried to acquire the lock yet.."); + } + + // change status to "being locked". This state transition is necessary to ensure syncing + // before granting the lock + String lockHolder = null; + boolean needToSyncQuorum = false; + if (currentMls != null) + needToSyncQuorum = currentMls.isNeedToSyncQuorum(); + + + newMls = new MusicLockState(MusicLockState.LockStatus.BEING_LOCKED, lockHolder, + needToSyncQuorum); + try { + getLockingServiceHandle().setLockState(key, newMls); + } catch (MusicLockingException e1) { + logger.error("Failed to set Lock state " + key + " " + e1); + } + logger.info("In acquire lock: Set lock state to being_locked"); + + // do syncing if this was a forced lock release + if (needToSyncQuorum) { + logger.info("In acquire lock: Since there was a forcible release, need to sync quorum!"); + syncQuorum(key); + } + + // change status to locked + lockHolder = lockId; + needToSyncQuorum = false; + newMls = new MusicLockState(MusicLockState.LockStatus.LOCKED, lockHolder, needToSyncQuorum); + try { + getLockingServiceHandle().setLockState(key, newMls); + } catch (MusicLockingException e) { + logger.error("Failed to set Lock state " + key + " " + e); + } + logger.info("In acquire lock: Set lock state to locked and assigned current lock ref " + + lockId + " as holder"); + return result; + } + + + + /** + * + * @param keyspaceName + * @param kspObject + * @return + * @throws Exception + */ + public boolean createKeyspace(String keyspaceName, JsonKeySpace kspObject) throws Exception { + return true; + } + + + private static void syncQuorum(String key) { + logger.info("Performing sync operation---"); + String[] splitString = key.split("\\."); + String keyspaceName = splitString[0]; + String tableName = splitString[1]; + String primaryKeyValue = splitString[2]; + PreparedQueryObject selectQuery = new PreparedQueryObject(); + PreparedQueryObject updateQuery = new PreparedQueryObject(); + + // get the primary key d + TableMetadata tableInfo = returnColumnMetadata(keyspaceName, tableName); + String primaryKeyName = tableInfo.getPrimaryKey().get(0).getName();// we only support single + // primary key + DataType primaryKeyType = tableInfo.getPrimaryKey().get(0).getType(); + String cqlFormattedPrimaryKeyValue = + MusicUtil.convertToCQLDataType(primaryKeyType, primaryKeyValue); + + // get the row of data from a quorum + selectQuery.appendQueryString("SELECT * FROM " + keyspaceName + "." + tableName + " WHERE " + + primaryKeyName + "= ?" + ";"); + selectQuery.addValue(cqlFormattedPrimaryKeyValue); + // String selectQuery = "SELECT * FROM "+keyspaceName+"."+tableName+ " WHERE + // "+primaryKeyName+"="+cqlFormattedPrimaryKeyValue+";"; + ResultSet results = null; + try { + results = getDSHandle().executeCriticalGet(selectQuery); + // write it back to a quorum + Row row = results.one(); + ColumnDefinitions colInfo = row.getColumnDefinitions(); + int totalColumns = colInfo.size(); + int counter = 1; + // String fieldValueString=""; + StringBuilder fieldValueString = new StringBuilder(""); + for (Definition definition : colInfo) { + String colName = definition.getName(); + if (colName.equals(primaryKeyName)) + continue; + DataType colType = definition.getType(); + Object valueObj = getDSHandle().getColValue(row, colName, colType); + String valueString = MusicUtil.convertToCQLDataType(colType, valueObj); + // fieldValueString = fieldValueString+ colName+"="+valueString; + fieldValueString.append(colName + " = ?"); + updateQuery.addValue(valueString); + if (counter != (totalColumns - 1)) + fieldValueString.append(","); + counter = counter + 1; + } + updateQuery.appendQueryString("UPDATE " + keyspaceName + "." + tableName + " SET " + + fieldValueString + " WHERE " + primaryKeyName + "= ? " + ";"); + updateQuery.addValue(cqlFormattedPrimaryKeyValue); + // String updateQuery = "UPDATE "+keyspaceName+"."+tableName+" SET "+fieldValueString+" + // WHERE "+primaryKeyName+"="+cqlFormattedPrimaryKeyValue+";"; + + getDSHandle().executePut(updateQuery, "critical"); + } catch (MusicServiceException | MusicQueryException e) { + logger.error("Failed to execute update query " + updateQuery + " " + e); + } + } + + + /** + * this function is mainly for the benchmarks to see the effect of lock deletion. + * + * @param keyspaceName + * @param tableName + * @param primaryKey + * @param queryObject + * @param conditionInfo + * @return + */ + public static ReturnType atomicPutWithDeleteLock(String keyspaceName, String tableName, + String primaryKey, PreparedQueryObject queryObject, Condition conditionInfo) { + long start = System.currentTimeMillis(); + String key = keyspaceName + "." + tableName + "." + primaryKey; + String lockId = createLockReference(key); + long lockCreationTime = System.currentTimeMillis(); + long leasePeriod = MusicUtil.getDefaultLockLeasePeriod(); + ReturnType lockAcqResult = acquireLockWithLease(key, lockId, leasePeriod); + long lockAcqTime = System.currentTimeMillis(); + if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { + logger.info("acquired lock with id " + lockId); + ReturnType criticalPutResult = criticalPut(keyspaceName, tableName, primaryKey, + queryObject, lockId, conditionInfo); + long criticalPutTime = System.currentTimeMillis(); + deleteLock(key); + long lockDeleteTime = System.currentTimeMillis(); + String timingInfo = "|lock creation time:" + (lockCreationTime - start) + + "|lock accquire time:" + (lockAcqTime - lockCreationTime) + + "|critical put time:" + (criticalPutTime - lockAcqTime) + + "|lock delete time:" + (lockDeleteTime - criticalPutTime) + "|"; + criticalPutResult.setTimingInfo(timingInfo); + return criticalPutResult; + } else { + logger.info("unable to acquire lock, id " + lockId); + deleteLock(key); + return lockAcqResult; + } + } + + /** + * + * @param query + * @return ResultSet + */ + public static ResultSet quorumGet(PreparedQueryObject query) { + ResultSet results = null; + try { + results = getDSHandle().executeCriticalGet(query); + } catch (MusicServiceException | MusicQueryException e) { + logger.error(e.getMessage()); + } + return results; + + } + + /** + * + * @param results + * @return + */ + public static Map<String, HashMap<String, Object>> marshallResults(ResultSet results) { + return getDSHandle().marshalData(results); + } + + /** + * + * @param lockName + * @return + */ + public static String whoseTurnIsIt(String lockName) { + + try { + return getLockingServiceHandle().whoseTurnIsIt("/" + lockName) + ""; + } catch (MusicLockingException e) { + logger.error("Failed whoseTurnIsIt " + lockName + " " + e); + } + return null; + + + } + + /** + * + * @param lockId + * @return + */ + public static String getLockNameFromId(String lockId) { + StringTokenizer st = new StringTokenizer(lockId); + return st.nextToken("$"); + } + + public static void destroyLockRef(String lockId) { + long start = System.currentTimeMillis(); + try { + getLockingServiceHandle().unlockAndDeleteId(lockId); + } catch (MusicLockingException e) { + logger.error("Failed to Destroy Lock Ref " + lockId + " " + e); + } + long end = System.currentTimeMillis(); + logger.info("Time taken to destroy lock reference:" + (end - start) + " ms"); + } + + public static MusicLockState releaseLock(String lockId, boolean voluntaryRelease) { + long start = System.currentTimeMillis(); + try { + getLockingServiceHandle().unlockAndDeleteId(lockId); + } catch (MusicLockingException e1) { + logger.error("Failed to release Lock " + lockId + " " + e1); + } + String lockName = getLockNameFromId(lockId); + MusicLockState mls; + String lockHolder = null; + if (voluntaryRelease) { + mls = new MusicLockState(MusicLockState.LockStatus.UNLOCKED, lockHolder); + logger.info("In unlock: lock voluntarily released for " + lockId); + } else { + boolean needToSyncQuorum = true; + mls = new MusicLockState(MusicLockState.LockStatus.UNLOCKED, lockHolder, + needToSyncQuorum); + logger.info("In unlock: lock forcibly released for " + lockId); + } + try { + getLockingServiceHandle().setLockState(lockName, mls); + } catch (MusicLockingException e) { + logger.error("Failed to release Lock " + lockName + " " + e); + } + long end = System.currentTimeMillis(); + logger.info("Time taken to release lock:" + (end - start) + " ms"); + return mls; + } + + /** + * + * @param lockName + */ + public static void deleteLock(String lockName) { + long start = System.currentTimeMillis(); + logger.info("Deleting lock for " + lockName); + try { + getLockingServiceHandle().deleteLock("/" + lockName); + } catch (MusicLockingException e) { + logger.error("Failed to Delete Lock " + lockName + " " + e); + } + long end = System.currentTimeMillis(); + logger.info("Time taken to delete lock:" + (end - start) + " ms"); + } + + + + /** + * + * @param keyspace + * @param tablename + * @return + */ + public static TableMetadata returnColumnMetadata(String keyspace, String tablename) { + return getDSHandle().returnColumnMetadata(keyspace, tablename); + } + + + /** + * + * @param nodeName + */ + public static void pureZkCreate(String nodeName) { + try { + getLockingServiceHandle().getzkLockHandle().createNode(nodeName); + } catch (MusicLockingException e) { + logger.error("Failed to get ZK Lock Handle " + e); + } + } + + /** + * + * @param nodeName + * @param data + */ + public static void pureZkWrite(String nodeName, byte[] data) { + long start = System.currentTimeMillis(); + logger.info("Performing zookeeper write to " + nodeName); + try { + getLockingServiceHandle().getzkLockHandle().setNodeData(nodeName, data); + } catch (MusicLockingException e) { + logger.error("Failed to get ZK Lock Handle " + e); + } + logger.info("Performed zookeeper write to " + nodeName); + long end = System.currentTimeMillis(); + logger.info("Time taken for the actual zk put:" + (end - start) + " ms"); + } + + /** + * + * @param nodeName + * @return + */ + public static byte[] pureZkRead(String nodeName) { + long start = System.currentTimeMillis(); + byte[] data = null; + try { + data = getLockingServiceHandle().getzkLockHandle().getNodeData(nodeName); + } catch (MusicLockingException e) { + logger.error("Failed to get ZK Lock Handle " + e); + } + long end = System.currentTimeMillis(); + logger.info("Time taken for the actual zk put:" + (end - start) + " ms"); + return data; + } + + + + // Prepared Query Additions. + + /** + * + * @param keyspaceName + * @param tableName + * @param primaryKey + * @param queryObject + * @return ReturnType + * @throws MusicServiceException + */ + public static ReturnType eventualPut(PreparedQueryObject queryObject) { + boolean result = false; + try { + result = getDSHandle().executePut(queryObject, MusicUtil.EVENTUAL); + } catch (MusicServiceException | MusicQueryException ex) { + logger.error(ex.getMessage() + " " + ex.getCause() + " " + ex); + } + if (result) { + return new ReturnType(ResultType.SUCCESS, "Success"); + } else { + return new ReturnType(ResultType.FAILURE, "Failure"); + } + } + + /** + * + * @param keyspaceName + * @param tableName + * @param primaryKey + * @param queryObject + * @param lockId + * @return + */ + public static ReturnType criticalPut(String keyspaceName, String tableName, String primaryKey, + PreparedQueryObject queryObject, String lockId, Condition conditionInfo) { + long start = System.currentTimeMillis(); + + try { + MusicLockState mls = getLockingServiceHandle() + .getLockState(keyspaceName + "." + tableName + "." + primaryKey); + if (mls.getLockHolder().equals(lockId) == true) { + if (conditionInfo != null)// check if condition is true + if (conditionInfo.testCondition() == false) + return new ReturnType(ResultType.FAILURE, + "Lock acquired but the condition is not true"); + getDSHandle().executePut(queryObject, MusicUtil.CRITICAL); + long end = System.currentTimeMillis(); + logger.info("Time taken for the critical put:" + (end - start) + " ms"); + return new ReturnType(ResultType.SUCCESS, "Update performed"); + } else + return new ReturnType(ResultType.FAILURE, + "Cannot perform operation since you are the not the lock holder"); + } catch (MusicQueryException | MusicServiceException | MusicLockingException e) { + logger.error(e.getMessage()); + return new ReturnType(ResultType.FAILURE, + "Exception thrown while doing the critical put, check sanctity of the row/conditions:\n" + + e.getMessage()); + } + + } + + /** + * + * @param queryObject + * @param consistency + * @return Boolean Indicates success or failure + * + * + */ + public static boolean nonKeyRelatedPut(PreparedQueryObject queryObject, String consistency) { + // this is mainly for some functions like keyspace creation etc which does not + // really need the bells and whistles of Music locking. + boolean result = false; + try { + result = getDSHandle().executePut(queryObject, consistency); + } catch (MusicQueryException | MusicServiceException ex) { + logger.error(ex.getMessage()); + } + return result; + } + + /** + * This method performs DDL operation on cassandra. + * + * @param queryObject query object containing prepared query and values + * @return ResultSet + */ + public static ResultSet get(PreparedQueryObject queryObject) { + ResultSet results = null; + try { + results = getDSHandle().executeEventualGet(queryObject); + } catch (MusicQueryException | MusicServiceException e) { + logger.error(e.getMessage()); + } + return results; + } + + /** + * This method performs DDL operations on cassandra, if the the resource is available. Lock ID + * is used to check if the resource is free. + * + * @param keyspaceName name of the keyspace + * @param tableName name of the table + * @param primaryKey primary key value + * @param queryObject query object containing prepared query and values + * @param lockId lock ID to check if the resource is free to perform the operation. + * @return ResultSet + */ + public static ResultSet criticalGet(String keyspaceName, String tableName, String primaryKey, + PreparedQueryObject queryObject, String lockId) throws MusicServiceException { + ResultSet results = null; + try { + MusicLockState mls = getLockingServiceHandle() + .getLockState(keyspaceName + "." + tableName + "." + primaryKey); + if (mls.getLockHolder().equals(lockId)) { + results = getDSHandle().executeCriticalGet(queryObject); + } else + throw new MusicServiceException("YOU DO NOT HAVE THE LOCK"); + } catch (MusicQueryException | MusicServiceException | MusicLockingException e) { + logger.error(e.getMessage()); + } + return results; + } + + /** + * This method performs DML operation on cassandra, when the lock of the dd is acquired. + * + * @param keyspaceName name of the keyspace + * @param tableName name of the table + * @param primaryKey primary key value + * @param queryObject query object containing prepared query and values + * @return ReturnType + */ + public static ReturnType atomicPut(String keyspaceName, String tableName, String primaryKey, + PreparedQueryObject queryObject, Condition conditionInfo) { + long start = System.currentTimeMillis(); + String key = keyspaceName + "." + tableName + "." + primaryKey; + String lockId = createLockReference(key); + long lockCreationTime = System.currentTimeMillis(); + long leasePeriod = MusicUtil.getDefaultLockLeasePeriod(); + ReturnType lockAcqResult = acquireLockWithLease(key, lockId, leasePeriod); + long lockAcqTime = System.currentTimeMillis(); + if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { + logger.info("acquired lock with id " + lockId); + ReturnType criticalPutResult = criticalPut(keyspaceName, tableName, primaryKey, + queryObject, lockId, conditionInfo); + long criticalPutTime = System.currentTimeMillis(); + boolean voluntaryRelease = true; + deleteLock(key); + long lockDeleteTime = System.currentTimeMillis(); + String timingInfo = "|lock creation time:" + (lockCreationTime - start) + + "|lock accquire time:" + (lockAcqTime - lockCreationTime) + + "|critical put time:" + (criticalPutTime - lockAcqTime) + + "|lock delete time:" + (lockDeleteTime - criticalPutTime) + "|"; + criticalPutResult.setTimingInfo(timingInfo); + return criticalPutResult; + } else { + logger.info("unable to acquire lock, id " + lockId); + destroyLockRef(lockId); + return lockAcqResult; + } + } + + + /** + * This method performs DDL operation on cassasndra, when the lock for the resource is acquired. + * + * @param keyspaceName name of the keyspace + * @param tableName name of the table + * @param primaryKey primary key value + * @param queryObject query object containing prepared query and values + * @return ResultSet + * @throws MusicServiceException + */ + public static ResultSet atomicGet(String keyspaceName, String tableName, String primaryKey, + PreparedQueryObject queryObject) throws MusicServiceException { + String key = keyspaceName + "." + tableName + "." + primaryKey; + String lockId = createLockReference(key); + long leasePeriod = MusicUtil.getDefaultLockLeasePeriod(); + ReturnType lockAcqResult = acquireLockWithLease(key, lockId, leasePeriod); + if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { + logger.info("acquired lock with id " + lockId); + ResultSet result = + criticalGet(keyspaceName, tableName, primaryKey, queryObject, lockId); + boolean voluntaryRelease = true; + releaseLock(lockId, voluntaryRelease); + return result; + } else { + logger.info("unable to acquire lock, id " + lockId); + return null; + } + } + + /** + * authenticate user logic + * + * @param nameSpace + * @param userId + * @param password + * @param keyspace + * @param aid + * @param operation + * @return + * @throws Exception + */ + public static Map<String, Object> autheticateUser(String nameSpace, String userId, + String password, String keyspace, String aid, String operation) + throws Exception { + Map<String, Object> resultMap = new HashMap<>(); + String uuid = null; + resultMap = CachingUtil.validateRequest(nameSpace, userId, password, keyspace, aid, + operation); + if (!resultMap.isEmpty()) + return resultMap; + if (aid == null && (userId == null || password == null)) { + logger.error("One or more required headers is missing. userId: " + userId + + " :: password: " + password); + resultMap.put("Exception", + "UserId and Password are mandatory for the operation " + operation); + return resultMap; + } + boolean isAAF = CachingUtil.isAAFApplication(nameSpace); + if (!isAAF && aid != null && aid.length() > 0) { // Non AAF app + resultMap = CachingUtil.authenticateAIDUser(aid, keyspace); + if (!resultMap.isEmpty()) + return resultMap; + } + if (isAAF && nameSpace != null && userId != null && password != null) { + boolean isValid = true; + try { + isValid = CachingUtil.authenticateAAFUser(nameSpace, userId, password, keyspace); + } catch (Exception e) { + logger.error("Got exception while AAF authentication for namespace " + nameSpace); + resultMap.put("Exception", e.getMessage()); + // return resultMap; + } + if (!isValid) { + logger.error("User not authenticated with AAF."); + resultMap.put("Exception", "User not authenticated..."); + // return resultMap; + } + if (!resultMap.isEmpty()) + return resultMap; + + } + + if (operation.equals("createKeySpace")) { + logger.info("AID is not provided. Creating new UUID for keyspace."); + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "select uuid from admin.keyspace_master where application_name=? and username=? and keyspace_name=? allow filtering"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), nameSpace)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), userId)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), + MusicUtil.DEFAULTKEYSPACENAME)); + + try { + Row rs = MusicCore.get(pQuery).one(); + uuid = rs.getUUID("uuid").toString(); + resultMap.put("uuid", "existing"); + } catch (Exception e) { + logger.info("No UUID found in DB. So creating new UUID."); + uuid = CachingUtil.generateUUID(); + resultMap.put("uuid", "new"); + } + + pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "INSERT into admin.keyspace_master (uuid, keyspace_name, application_name, is_api, " + + "password, username, is_aaf) values (?,?,?,?,?,?,?)"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), uuid)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), keyspace)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), nameSpace)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), "True")); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), password)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), userId)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), isAAF)); + CachingUtil.updateMusicCache(uuid, keyspace); + MusicCore.eventualPut(pQuery); + resultMap.put("aid", uuid); + } + + return resultMap; + } +} diff --git a/src/main/java/org/onap/music/main/MusicDigest.java b/src/main/java/org/onap/music/main/MusicDigest.java new file mode 100644 index 00000000..893cb51f --- /dev/null +++ b/src/main/java/org/onap/music/main/MusicDigest.java @@ -0,0 +1,78 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.main; + +/** + * + * + */ +public class MusicDigest { + private String evPutStatus; + private String vectorTs; + + /** + * @param evPutStatus + * @param vectorTs + */ + public MusicDigest(String evPutStatus, String vectorTs) { + this.evPutStatus = evPutStatus; + this.vectorTs = vectorTs; + } + + /** + * @return + */ + public String getEvPutStatus() { + return evPutStatus; + } + + /** + * @param evPutStatus + */ + public void setEvPutStatus(String evPutStatus) { + this.evPutStatus = evPutStatus; + } + + /** + * @return + */ + public String getVectorTs() { + return vectorTs; + } + + /** + * @param vectorTs + */ + public void setVectorTs(String vectorTs) { + this.vectorTs = vectorTs; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + public String toString() { + return vectorTs + "|" + evPutStatus; + } +} + diff --git a/src/main/java/org/onap/music/main/MusicUtil.java b/src/main/java/org/onap/music/main/MusicUtil.java new file mode 100755 index 00000000..61d428d1 --- /dev/null +++ b/src/main/java/org/onap/music/main/MusicUtil.java @@ -0,0 +1,465 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.main; + + +import java.io.File; +import java.io.FileNotFoundException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.UUID; +import org.onap.music.datastore.PreparedQueryObject; +import org.onap.music.eelf.logging.EELFLoggerDelegate; +import com.datastax.driver.core.DataType; + +/** + * @author nelson24 + * + * Properties This will take Properties and load them into MusicUtil. This is a hack for + * now. Eventually it would bebest to do this in another way. + * + */ +public class MusicUtil { + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MusicUtil.class); + + private static int myId = 0; + private static ArrayList<String> allIds = new ArrayList<String>(); + private static String publicIp = ""; + private static ArrayList<String> allPublicIps = new ArrayList<String>(); + private static String myZkHost = "localhost"; + private static String myCassaHost = "localhost"; + private static String defaultMusicIp = "localhost"; + private static boolean debug = true; + private static String version = "2.3.0"; + public static String musicRestIp = "localhost"; + private static String musicPropertiesFilePath = "/opt/app/music/etc/music.properties"; + private static long defaultLockLeasePeriod = 6000; + private static final String[] propKeys = new String[] {"zookeeper.host", "cassandra.host", + "music.ip", "debug", "version", "music.rest.ip", "music.properties", + "lock.lease.period", "id", "all.ids", "public.ip", "all.pubic.ips", + "cassandra.user", "cassandra.password", "aaf.endpoint.url"}; + + public static final String ATOMIC = "atomic"; + public static final String EVENTUAL = "eventual"; + public static final String CRITICAL = "critical"; + public static final String DEFAULTKEYSPACENAME = "TBD"; + private static String cassName = "cassandra"; + private static String cassPwd = "cassandra"; + private static String aafEndpointUrl = null; + + /** + * @return the cassName + */ + public static String getCassName() { + return cassName; + } + + /** + * @return the cassPwd + */ + public static String getCassPwd() { + return cassPwd; + } + + /** + * @return the aafEndpointUrl + */ + public static String getAafEndpointUrl() { + return aafEndpointUrl; + } + + /** + * + * @param aafEndpointUrl + */ + public static void setAafEndpointUrl(String aafEndpointUrl) { + MusicUtil.aafEndpointUrl = aafEndpointUrl; + } + + /** + * + * @return + */ + public static int getMyId() { + return myId; + } + + /** + * + * @param myId + */ + public static void setMyId(int myId) { + MusicUtil.myId = myId; + } + + + /** + * + * @return + */ + public static ArrayList<String> getAllIds() { + return allIds; + } + + /** + * + * @param allIds + */ + public static void setAllIds(List<String> allIds) { + MusicUtil.allIds = (ArrayList<String>) allIds; + } + + /** + * + * @return + */ + public static String getPublicIp() { + return publicIp; + } + + /** + * + * @param publicIp + */ + public static void setPublicIp(String publicIp) { + MusicUtil.publicIp = publicIp; + } + + /** + * + * @return + */ + public static ArrayList<String> getAllPublicIps() { + return allPublicIps; + } + + /** + * + * @param allPublicIps + */ + public static void setAllPublicIps(List<String> allPublicIps) { + MusicUtil.allPublicIps = (ArrayList<String>) allPublicIps; + } + + /** + * Returns An array of property names that should be in the Properties files. + * + * @return + */ + public static String[] getPropkeys() { + return propKeys; + } + + /** + * Get MusicRestIp - default = localhost property file value - music.rest.ip + * + * @return + */ + public static String getMusicRestIp() { + return musicRestIp; + } + + /** + * Set MusicRestIp + * + * @param musicRestIp + */ + public static void setMusicRestIp(String musicRestIp) { + MusicUtil.musicRestIp = musicRestIp; + } + + /** + * Get MusicPropertiesFilePath - Default = /opt/music/music.properties property file value - + * music.properties + * + * @return + */ + public static String getMusicPropertiesFilePath() { + return musicPropertiesFilePath; + } + + /** + * Set MusicPropertiesFilePath + * + * @param musicPropertiesFilePath + */ + public static void setMusicPropertiesFilePath(String musicPropertiesFilePath) { + MusicUtil.musicPropertiesFilePath = musicPropertiesFilePath; + } + + /** + * Get DefaultLockLeasePeriod - Default = 6000 property file value - lock.lease.period + * + * @return + */ + public static long getDefaultLockLeasePeriod() { + return defaultLockLeasePeriod; + } + + /** + * Set DefaultLockLeasePeriod + * + * @param defaultLockLeasePeriod + */ + public static void setDefaultLockLeasePeriod(long defaultLockLeasePeriod) { + MusicUtil.defaultLockLeasePeriod = defaultLockLeasePeriod; + } + + /** + * Set Debug + * + * @param debug + */ + public static void setDebug(boolean debug) { + MusicUtil.debug = debug; + } + + /** + * Is Debug - Default = true property file value - debug + * + * @return + */ + public static boolean isDebug() { + return debug; + } + + /** + * Set Version + * + * @param version + */ + public static void setVersion(String version) { + MusicUtil.version = version; + } + + /** + * Return the version property file value - version + * + * @return + */ + public static String getVersion() { + return version; + } + + /** + * Get MyZkHost - Zookeeper Hostname - Default = localhost property file value - zookeeper.host + * + * @return + */ + public static String getMyZkHost() { + return myZkHost; + } + + /** + * Set MyZkHost - Zookeeper Hostname + * + * @param myZkHost + */ + public static void setMyZkHost(String myZkHost) { + MusicUtil.myZkHost = myZkHost; + } + + /** + * Get MyCassHost - Cassandra Hostname - Default = localhost property file value - + * cassandra.host + * + * @return + */ + public static String getMyCassaHost() { + return myCassaHost; + } + + /** + * Set MyCassHost - Cassandra Hostname + * + * @param myCassaHost + */ + public static void setMyCassaHost(String myCassaHost) { + MusicUtil.myCassaHost = myCassaHost; + } + + /** + * Get DefaultMusicIp - Default = localhost property file value - music.ip + * + * @return + */ + public static String getDefaultMusicIp() { + return defaultMusicIp; + } + + /** + * Set DefaultMusicIp + * + * @param defaultMusicIp + */ + public static void setDefaultMusicIp(String defaultMusicIp) { + MusicUtil.defaultMusicIp = defaultMusicIp; + } + + /** + * + * @return + */ + public static String getTestType() { + String testType = ""; + try { + Scanner fileScanner = new Scanner(new File("")); + testType = fileScanner.next();// ignore the my id line + String batchSize = fileScanner.next();// ignore the my public ip line + fileScanner.close(); + } catch (FileNotFoundException e) { + logger.error(EELFLoggerDelegate.errorLogger, e.getMessage()); + } + return testType; + + } + + /** + * + * @param time + */ + public static void sleep(long time) { + try { + Thread.sleep(time); + } catch (InterruptedException e) { + logger.error(EELFLoggerDelegate.errorLogger, e.getMessage()); + } + } + + /** + * Utility function to check if the query object is valid. + * + * @param withparams + * @param queryObject + * @return + */ + public static boolean isValidQueryObject(boolean withparams, PreparedQueryObject queryObject) { + if (withparams) { + int noOfValues = queryObject.getValues().size(); + int noOfParams = 0; + char[] temp = queryObject.getQuery().toCharArray(); + for (int i = 0; i < temp.length; i++) { + if (temp[i] == '?') + noOfParams++; + } + return (noOfValues == noOfParams); + } else { + return !queryObject.getQuery().isEmpty(); + } + + } + + public static void setCassName(String cassName) { + MusicUtil.cassName = cassName; + } + + public static void setCassPwd(String cassPwd) { + MusicUtil.cassPwd = cassPwd; + } + + public static String convertToCQLDataType(DataType type, Object valueObj) { + + String value = ""; + switch (type.getName()) { + case UUID: + value = valueObj + ""; + break; + case TEXT: + case VARCHAR: + String valueString = valueObj + ""; + valueString = valueString.replace("'", "''"); + value = "'" + valueString + "'"; + break; + case MAP: { + Map<String, Object> otMap = (Map<String, Object>) valueObj; + value = "{" + jsonMaptoSqlString(otMap, ",") + "}"; + break; + } + default: + value = valueObj + ""; + break; + } + return value; + } + + + /** + * + * @param colType + * @param valueObj + * @return + * @throws Exception + */ + public static Object convertToActualDataType(DataType colType, Object valueObj) { + String valueObjString = valueObj + ""; + switch (colType.getName()) { + case UUID: + return UUID.fromString(valueObjString); + case VARINT: + return BigInteger.valueOf(Long.parseLong(valueObjString)); + case BIGINT: + return Long.parseLong(valueObjString); + case INT: + return Integer.parseInt(valueObjString); + case FLOAT: + return Float.parseFloat(valueObjString); + case DOUBLE: + return Double.parseDouble(valueObjString); + case BOOLEAN: + return Boolean.parseBoolean(valueObjString); + case MAP: + return (Map<String, Object>) valueObj; + default: + return valueObjString; + } + } + + + /** + * + * Utility function to parse json map into sql like string + * + * @param jMap + * @param lineDelimiter + * @return + */ + + public static String jsonMaptoSqlString(Map<String, Object> jMap, String lineDelimiter) { + StringBuilder sqlString = new StringBuilder(); + int counter = 0; + for (Map.Entry<String, Object> entry : jMap.entrySet()) { + Object ot = entry.getValue(); + String value = ot + ""; + if (ot instanceof String) { + value = "'" + value.replace("'", "''") + "'"; + } + sqlString.append("'" + entry.getKey() + "':" + value); + if (counter != jMap.size() - 1) + sqlString.append(lineDelimiter); + counter = counter + 1; + } + return sqlString.toString(); + } +} diff --git a/src/main/java/org/onap/music/main/PropertiesListener.java b/src/main/java/org/onap/music/main/PropertiesListener.java new file mode 100755 index 00000000..de231542 --- /dev/null +++ b/src/main/java/org/onap/music/main/PropertiesListener.java @@ -0,0 +1,147 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.main; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Properties; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.annotation.WebListener; +import org.onap.music.eelf.logging.EELFLoggerDelegate; + +@WebListener +public class PropertiesListener implements ServletContextListener { + private Properties prop; + + private static EELFLoggerDelegate logger = + EELFLoggerDelegate.getLogger(PropertiesListener.class); + + @Override + public void contextInitialized(ServletContextEvent servletContextEvent) { + prop = new Properties(); + Properties projectProp = new Properties(); + URL resource = getClass().getResource("/"); + String musicPropertiesFilePath = resource.getPath().replace("WEB-INF/classes/", + "WEB-INF/classes/project.properties"); + + // Open the file + try { + InputStream musicProps = null; + projectProp.load(new FileInputStream(musicPropertiesFilePath)); + if (projectProp.containsKey("music.properties")) { + musicProps = new FileInputStream(projectProp.getProperty("music.properties")); + } else { + musicProps = new FileInputStream(MusicUtil.getMusicPropertiesFilePath()); + } + prop.load(musicProps); + musicProps.close(); + prop.putAll(projectProp); + String[] propKeys = MusicUtil.getPropkeys(); + for (int k = 0; k < propKeys.length; k++) { + String key = propKeys[k]; + if (prop.containsKey(key) && prop.get(key) != null) { + logger.info(key + " : " + prop.getProperty(key)); + switch (key) { + case "zookeeper.host": + MusicUtil.setMyZkHost(prop.getProperty(key)); + break; + case "cassandra.host": + MusicUtil.setMyCassaHost(prop.getProperty(key)); + break; + case "music.ip": + MusicUtil.setDefaultMusicIp(prop.getProperty(key)); + break; + case "debug": + MusicUtil.setDebug(Boolean + .getBoolean(prop.getProperty(key).toLowerCase())); + break; + case "version": + MusicUtil.setVersion(prop.getProperty(key)); + break; + case "music.rest.ip": + MusicUtil.setMusicRestIp(prop.getProperty(key)); + break; + case "music.properties": + MusicUtil.setMusicPropertiesFilePath(prop.getProperty(key)); + break; + case "lock.lease.period": + MusicUtil.setDefaultLockLeasePeriod( + Long.parseLong(prop.getProperty(key))); + break; + case "my.id": + MusicUtil.setMyId(Integer.parseInt(prop.getProperty(key))); + break; + case "all.ids": + String[] ids = prop.getProperty(key).split(":"); + MusicUtil.setAllIds(new ArrayList<String>(Arrays.asList(ids))); + break; + case "public.ip": + MusicUtil.setPublicIp(prop.getProperty(key)); + break; + case "all.public.ips": + String[] ips = prop.getProperty(key).split(":"); + if (ips.length == 1) { + // Future use + } else if (ips.length > 1) { + MusicUtil.setAllPublicIps( + new ArrayList<String>(Arrays.asList(ips))); + } + break; + case "cassandra.user": + MusicUtil.setCassName(prop.getProperty(key)); + break; + case "cassandra.password": + MusicUtil.setCassPwd(prop.getProperty(key)); + break; + case "aaf.endpoint.url": + MusicUtil.setAafEndpointUrl(prop.getProperty(key)); + break; + default: + logger.error(EELFLoggerDelegate.errorLogger, + "No case found for " + key); + } + } + } + } catch (IOException e) { + logger.error(EELFLoggerDelegate.errorLogger, e.getMessage()); + } + + logger.info(EELFLoggerDelegate.applicationLogger, + "Starting MUSIC " + MusicUtil.getVersion() + " on node with id " + + MusicUtil.getMyId() + " and public ip " + + MusicUtil.getPublicIp() + "..."); + logger.info(EELFLoggerDelegate.applicationLogger, + "List of all MUSIC ids:" + MusicUtil.getAllIds().toString()); + logger.info(EELFLoggerDelegate.applicationLogger, + "List of all MUSIC public ips:" + MusicUtil.getAllPublicIps().toString()); + } + + @Override + public void contextDestroyed(ServletContextEvent servletContextEvent) { + prop = null; + } +} diff --git a/src/main/java/org/onap/music/main/ResultType.java b/src/main/java/org/onap/music/main/ResultType.java new file mode 100644 index 00000000..f19ada4a --- /dev/null +++ b/src/main/java/org/onap/music/main/ResultType.java @@ -0,0 +1,39 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.main; + +public enum ResultType { + SUCCESS("Success"), FAILURE("Failure"); + + private String result; + + ResultType(String result) { + this.result = result; + } + + public String getResult() { + return result; + } + +} + + diff --git a/src/main/java/org/onap/music/main/ReturnType.java b/src/main/java/org/onap/music/main/ReturnType.java new file mode 100644 index 00000000..1453a1bf --- /dev/null +++ b/src/main/java/org/onap/music/main/ReturnType.java @@ -0,0 +1,74 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.main; + +import java.util.HashMap; +import java.util.Map; + +public class ReturnType { + private ResultType result; + private String message; + + public ReturnType(ResultType result, String message) { + super(); + this.result = result; + this.message = message; + } + + public String getTimingInfo() { + return timingInfo; + } + + public void setTimingInfo(String timingInfo) { + this.timingInfo = timingInfo; + } + + private String timingInfo; + + public ResultType getResult() { + return result; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String toJson() { + return "{ \"result\":\"" + result.getResult() + "\", \"message\":\"" + message + "\"}"; + } + + public String toString() { + return result + " | " + message; + } + + public Map<String, Object> toMap() { + Map<String, Object> newMap = new HashMap<>(); + newMap.put("result", result.getResult()); + newMap.put("message", message); + return newMap; + } + +} diff --git a/src/main/java/org/onap/music/response/jsonobjects/JsonLockResponse.java b/src/main/java/org/onap/music/response/jsonobjects/JsonLockResponse.java new file mode 100644 index 00000000..875fbbba --- /dev/null +++ b/src/main/java/org/onap/music/response/jsonobjects/JsonLockResponse.java @@ -0,0 +1,258 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.response.jsonobjects; + +import java.util.HashMap; +import java.util.Map; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonResponse", description = "General Response JSON") +public class JsonLockResponse { + + private String status = ""; + private String error = ""; + private String message = ""; + private String lock = ""; + private String lockStatus = ""; + private String lockHolder = ""; + private String lockLease = ""; + + /** + * + * @param status + * @param error + * @param lock + */ + public JsonLockResponse(String status, String error, String lock) { + this.status = fixStatus(status); + this.error = error; + this.lock = lock; + } + + /** + * + * @param status + * @param error + * @param lock + * @param lockStatus + * @param lockHolder + */ + public JsonLockResponse(String status, String error, String lock, String lockStatus, + String lockHolder) { + this.status = fixStatus(status); + this.error = error; + this.lock = lock; + this.lockStatus = lockStatus; + this.lockHolder = lockHolder; + } + + /** + * + * @param status + * @param error + * @param lock + * @param lockStatus + * @param lockHolder + * @param lockLease + */ + public JsonLockResponse(String status, String error, String lock, String lockStatus, + String lockHolder, String lockLease) { + this.status = fixStatus(status); + this.error = error; + this.lock = lock; + this.lockStatus = lockStatus; + this.lockHolder = lockHolder; + } + + + /** + * Lock + * + * @return + */ + public String getLock() { + return lock; + } + + /** + * + * @param lock + */ + public void setLock(String lock) { + this.lock = lock; + } + + /** + * + */ + public JsonLockResponse() { + this.status = ""; + this.error = ""; + } + + /** + * + * @param statusIn + * @return + */ + private String fixStatus(String statusIn) { + if (statusIn.equalsIgnoreCase("false")) { + return "FAILURE"; + } + return "SUCCESS"; + } + + /** + * + * @return + */ + @ApiModelProperty(value = "Overall status of the response.", + allowableValues = "Success,Failure") + public String getStatus() { + return status; + } + + /** + * + * @param status + */ + public void setStatus(String status) { + this.status = fixStatus(status); + } + + /** + * + * @return the error + */ + @ApiModelProperty(value = "Error value") + public String getError() { + return error; + } + + /** + * + * @param error + */ + public void setError(String error) { + this.error = error; + } + + /** + * + * @return the message + */ + @ApiModelProperty(value = "Message if any need to be conveyed about the lock") + public String getMessage() { + return message; + } + + /** + * + * @param message + */ + public void setMessage(String message) { + this.message = message; + } + + /** + * + * @return the lockStatus + */ + @ApiModelProperty(value = "Status of the lock", + allowableValues = "UNLOCKED,BEING_LOCKED,LOCKED") + public String getLockStatus() { + return lockStatus; + } + + /** + * + * @param lockStatus + */ + public void setLockStatus(String lockStatus) { + this.lockStatus = lockStatus; + } + + /** + * + * + * @return the lockHolder + */ + @ApiModelProperty(value = "Holder of the Lock") + public String getLockHolder() { + return lockHolder; + } + + /** + * + * @param lockHolder + */ + public void setLockHolder(String lockHolder) { + this.lockHolder = lockHolder; + } + + + + /** + * @return the lockLease + */ + public String getLockLease() { + return lockLease; + } + + /** + * @param lockLease the lockLease to set + */ + public void setLockLease(String lockLease) { + this.lockLease = lockLease; + } + + /** + * Convert to Map + * + * @return + */ + public Map<String, Object> toMap() { + Map<String, Object> newMap = new HashMap<>(); + Map<String, Object> lockMap = new HashMap<>(); + lockMap.put("lock-status", lockStatus); + lockMap.put("lock", lock); + lockMap.put("message", message); + lockMap.put("lock-holder", lockHolder); + lockMap.put("lock-lease", lockLease); + newMap.put("status", status); + newMap.put("error", error); + newMap.put("lock", lockMap); + return newMap; + } + + /** + * Convert to String + */ + @Override + public String toString() { + return "JsonLockResponse [status=" + status + ", error=" + error + ", message=" + message + + ", lock=" + lock + ", lockStatus=" + lockStatus + ", lockHolder=" + + lockHolder + "]"; + } + +} diff --git a/src/main/java/org/onap/music/response/jsonobjects/JsonResponse.java b/src/main/java/org/onap/music/response/jsonobjects/JsonResponse.java new file mode 100644 index 00000000..d44f9fe7 --- /dev/null +++ b/src/main/java/org/onap/music/response/jsonobjects/JsonResponse.java @@ -0,0 +1,95 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.response.jsonobjects; + + +import java.util.HashMap; +import java.util.Map; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonResponse", description = "General Response JSON") +public class JsonResponse { + + private Boolean status = false; + private String error = ""; + private String version = ""; + + public JsonResponse(Boolean status, String error, String version) { + this.status = status; + this.error = error; + this.version = version; + } + + public JsonResponse() { + this.status = false; + this.error = ""; + this.version = ""; + } + + @ApiModelProperty(value = "Status value") + public Boolean getStatus() { + return status; + } + + /** + * + * @param statusIn + * @return + */ + private String fixStatus(String statusIn) { + if (statusIn.equalsIgnoreCase("false")) { + return "FAILURE"; + } + return "SUCCESS"; + } + + public void setStatus(Boolean status) { + this.status = status; + } + + @ApiModelProperty(value = "Error value") + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + + @ApiModelProperty(value = "Version value") + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public Map<String, Object> toMap() { + Map<String, Object> newMap = new HashMap<>(); + newMap.put("status", fixStatus(String.valueOf(status))); + newMap.put("error", error); + newMap.put("version", version); + return newMap; + } +} diff --git a/src/main/java/org/onap/music/rest/RestMusicAdminAPI.java b/src/main/java/org/onap/music/rest/RestMusicAdminAPI.java new file mode 100755 index 00000000..87a3a1ba --- /dev/null +++ b/src/main/java/org/onap/music/rest/RestMusicAdminAPI.java @@ -0,0 +1,305 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.rest; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import org.onap.music.datastore.PreparedQueryObject; +import org.onap.music.datastore.jsonobjects.JsonOnboard; +import org.onap.music.main.CachingUtil; +import org.onap.music.main.MusicCore; +import org.onap.music.main.MusicUtil; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; + +@Path("/v{version: [0-9]+}/admin") +// @Path("/admin") +@Api(value = "Admin Api", hidden = true) +public class RestMusicAdminAPI { + private static EELFLogger logger = EELFManager.getInstance().getLogger(RestMusicAdminAPI.class); + + /* + * API to onboard an application with MUSIC. This is the mandatory first step. + * + */ + @POST + @Path("/onboardAppWithMusic") + @ApiOperation(value = "Onboard application", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map<String, Object> onboardAppWithMusic(JsonOnboard jsonObj, + @Context HttpServletResponse response) throws Exception { + Map<String, Object> resultMap = new HashMap<>(); + String appName = jsonObj.getAppname(); + String userId = jsonObj.getUserId(); + String isAAF = jsonObj.getIsAAF(); + String password = jsonObj.getPassword(); + response.addHeader("X-latestVersion", MusicUtil.getVersion()); + if (appName == null || userId == null || isAAF == null || password == null) { + resultMap.put("Exception", + "Please check the request parameters. Some of the required values appName(ns), userId, password, isAAF are missing."); + return resultMap; + } + + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "select uuid from admin.keyspace_master where application_name = ? allow filtering"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName)); + ResultSet rs = MusicCore.get(pQuery); + if (!rs.all().isEmpty()) { + resultMap.put("Exception", "Your application " + appName + + " has already been onboarded. Please contact admin."); + return resultMap; + } + + pQuery = new PreparedQueryObject(); + String uuid = CachingUtil.generateUUID(); + pQuery.appendQueryString( + "INSERT INTO admin.keyspace_master (uuid, keyspace_name, application_name, is_api, " + + "password, username, is_aaf) VALUES (?,?,?,?,?,?,?)"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), uuid)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), + MusicUtil.DEFAULTKEYSPACENAME)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), "True")); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), password)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), userId)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), isAAF)); + + String returnStr = MusicCore.eventualPut(pQuery).toString(); + if (returnStr.contains("Failure")) { + resultMap.put("Exception", + "Oops. Something wrong with onboarding process. Please retry later or contact admin."); + return resultMap; + } + CachingUtil.updateisAAFCache(appName, isAAF); + resultMap.put("Success", "Your application " + appName + " has been onboarded with MUSIC."); + resultMap.put("Generated AID", uuid); + return resultMap; + } + + + /* + * API to onboard an application with MUSIC. This is the mandatory first step. + * + */ + @GET + @Path("/onboardAppWithMusic") + @ApiOperation(value = "Onboard application", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map<String, Object> getOnboardedInfo( + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String uuid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String appName, + @Context HttpServletResponse response) throws Exception { + Map<String, Object> resultMap = new HashMap<>(); + + response.addHeader("X-latestVersion", MusicUtil.getVersion()); + if (appName == null && uuid == null) { + resultMap.put("Exception", + "Please check the request parameters. Some of the required values appName(ns), aid are missing."); + return resultMap; + } + + PreparedQueryObject pQuery = new PreparedQueryObject(); + + String cql = "select uuid, keyspace_name from admin.keyspace_master where "; + if (appName != null) + cql = cql + "application_name = ?"; + else if (uuid != null) + cql = cql + "uuid = ?"; + cql = cql + " allow filtering"; + System.out.println("Get OnboardingInfo CQL: " + cql); + pQuery.appendQueryString(cql); + if (appName != null) + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName)); + else if (uuid != null) + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), uuid)); + ResultSet rs = MusicCore.get(pQuery); + Iterator<Row> it = rs.iterator(); + while (it.hasNext()) { + Row row = (Row) it.next(); + resultMap.put(row.getString("keyspace_name"), row.getUUID("uuid")); + } + if (resultMap.isEmpty()) + resultMap.put("ERROR", "Application is not onboarded. Please contact admin."); + return resultMap; + } + + + @DELETE + @Path("/onboardAppWithMusic") + @ApiOperation(value = "Delete Onboard application", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map<String, Object> deleteOnboardApp(JsonOnboard jsonObj, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @Context HttpServletResponse response) throws Exception { + Map<String, Object> resultMap = new HashMap<>(); + response.addHeader("X-latestVersion", MusicUtil.getVersion()); + String appName = jsonObj.getAppname(); + PreparedQueryObject pQuery = new PreparedQueryObject(); + long count = 0; + if (appName == null && aid == null) { + resultMap.put("Exception", "Please make sure either appName(ns) or Aid is present"); + return resultMap; + } + if (aid != null) { + pQuery.appendQueryString( + "select count(*) as count from admin.keyspace_master where uuid = ?"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), + UUID.fromString(aid))); + Row row = MusicCore.get(pQuery).one(); + if (row != null) { + count = row.getLong(0); + } + + if (count == 0) { + resultMap.put("Failure", "Please verify your AID."); + return resultMap; + } else { + pQuery = new PreparedQueryObject(); + pQuery.appendQueryString("delete from admin.keyspace_master where uuid = ?"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), + UUID.fromString(aid))); + String result = MusicCore.eventualPut(pQuery).toString(); + if (result.toLowerCase().contains("success")) { + resultMap.put("Success", "Your application has been deleted."); + return resultMap; + } else { + resultMap.put("Failure", "Please verify your AID."); + return resultMap; + } + } + + } + pQuery.appendQueryString( + "select uuid from admin.keyspace_master where application_name = ? allow filtering"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName)); + ResultSet rs = MusicCore.get(pQuery); + List<Row> rows = rs.all(); + String uuid = null; + if (rows.size() == 0) { + resultMap.put("Exception", + "Application not found. Please make sure Application exists."); + return resultMap; + } else if (rows.size() == 1) { + uuid = rows.get(0).getUUID("uuid").toString(); + pQuery = new PreparedQueryObject(); + pQuery.appendQueryString("delete from admin.keyspace_master where uuid = ?"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), + UUID.fromString(uuid))); + MusicCore.eventualPut(pQuery); + resultMap.put("Success", "Your application " + appName + " has been deleted."); + return resultMap; + } else { + resultMap.put("Failure", "Please provide UUID for the application."); + } + + return resultMap; + } + + + @PUT + @Path("/onboardAppWithMusic") + @ApiOperation(value = "Update Onboard application", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map<String, Object> updateOnboardApp(JsonOnboard jsonObj, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @Context HttpServletResponse response) throws Exception { + Map<String, Object> resultMap = new HashMap<>(); + response.addHeader("X-latestVersion", MusicUtil.getVersion()); + String appName = jsonObj.getAppname(); + String userId = jsonObj.getUserId(); + String isAAF = jsonObj.getIsAAF(); + String password = jsonObj.getPassword(); + String consistency = "eventual"; + PreparedQueryObject pQuery = new PreparedQueryObject(); + + if (aid == null) { + resultMap.put("Exception", "Please make sure Aid is present"); + return resultMap; + } + + if (appName == null && userId == null && password == null && isAAF == null) { + resultMap.put("Exception", + "No parameters found to update. Please update atleast one parameter."); + return resultMap; + } + + StringBuilder preCql = new StringBuilder("UPDATE admin.keyspace_master SET "); + if (appName != null) + preCql.append(" application_name = ?,"); + if (userId != null) + preCql.append(" username = ?,"); + if (password != null) + preCql.append(" password = ?,"); + if (isAAF != null) + preCql.append(" is_aaf = ?,"); + preCql.deleteCharAt(preCql.length() - 1); + preCql.append(" WHERE uuid = ?"); + pQuery.appendQueryString(preCql.toString()); + if (appName != null) + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName)); + if (userId != null) + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), userId)); + if (password != null) + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), password)); + if (isAAF != null) + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), isAAF)); + + + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), UUID.fromString(aid))); + Boolean result = MusicCore.nonKeyRelatedPut(pQuery, consistency); + + if (result) { + resultMap.put("Success", "Your application has been updated successfully"); + } else { + resultMap.put("Exception", + "Oops. Spomething went wrong. Please make sure Aid is correct and application is onboarded"); + } + + return resultMap; + } +} diff --git a/src/main/java/org/onap/music/rest/RestMusicBmAPI.java b/src/main/java/org/onap/music/rest/RestMusicBmAPI.java new file mode 100644 index 00000000..90b82229 --- /dev/null +++ b/src/main/java/org/onap/music/rest/RestMusicBmAPI.java @@ -0,0 +1,313 @@ +/* + * ============LICENSE_START========================================== org.onap.music + * =================================================================== Copyright (c) 2017 AT&T + * Intellectual Property =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.rest; + +import java.util.List; +import java.util.Map; +import java.util.UUID; +import javax.ws.rs.Consumes; +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.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriInfo; +import org.apache.log4j.Logger; +import org.onap.music.datastore.jsonobjects.JsonInsert; +import org.onap.music.datastore.jsonobjects.JsonOnboard; +import org.onap.music.datastore.jsonobjects.JsonUpdate; +import org.onap.music.main.CachingUtil; +import org.onap.music.main.MusicCore; +import org.onap.music.main.MusicUtil; +import org.onap.music.main.ResultType; +import org.onap.music.main.ReturnType; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.onap.music.datastore.PreparedQueryObject; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.TableMetadata; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; + +/* + * These are functions created purely for benchmarking purposes. Commented out Swagger - This should + * be undocumented API + * + */ +@Path("/v{version: [0-9]+}/benchmarks/") +@Api(value = "Benchmark API", hidden = true) +public class RestMusicBmAPI { + private static EELFLogger logger = EELFManager.getInstance().getLogger(RestMusicBmAPI.class); + + // pure zk calls... + + /** + * + * @param nodeName + * @throws Exception + */ + @POST + @Path("/purezk/{name}") + @Consumes(MediaType.APPLICATION_JSON) + public void pureZkCreate(@PathParam("name") String nodeName) throws Exception { + MusicCore.pureZkCreate("/" + nodeName); + } + + + /** + * + * @param insObj + * @param nodeName + * @throws Exception + */ + @PUT + @Path("/purezk/{name}") + @Consumes(MediaType.APPLICATION_JSON) + public void pureZkUpdate(JsonInsert insObj, @PathParam("name") String nodeName) + throws Exception { + logger.info("--------------Zk normal update-------------------------"); + long start = System.currentTimeMillis(); + MusicCore.pureZkWrite(nodeName, insObj.serialize()); + long end = System.currentTimeMillis(); + logger.info("Total time taken for Zk normal update:" + (end - start) + " ms"); + } + + /** + * + * @param nodeName + * @return + * @throws Exception + */ + @GET + @Path("/purezk/{name}") + @Consumes(MediaType.TEXT_PLAIN) + public byte[] pureZkGet(@PathParam("name") String nodeName) throws Exception { + return MusicCore.pureZkRead(nodeName); + } + + /** + * + * @param insObj + * @param lockName + * @param nodeName + * @throws Exception + */ + @PUT + @Path("/purezk/atomic/{lockname}/{name}") + @Consumes(MediaType.APPLICATION_JSON) + public void pureZkAtomicPut(JsonInsert updateObj, @PathParam("lockname") String lockname, + @PathParam("name") String nodeName) throws Exception { + long startTime = System.currentTimeMillis(); + String operationId = UUID.randomUUID().toString();// just for debugging purposes. + String consistency = updateObj.getConsistencyInfo().get("type"); + + logger.info("--------------Zookeeper " + consistency + " update-" + operationId + + "-------------------------"); + + byte[] data = updateObj.serialize(); + long jsonParseCompletionTime = System.currentTimeMillis(); + + String lockId = MusicCore.createLockReference(lockname); + + long lockCreationTime = System.currentTimeMillis(); + + long leasePeriod = MusicUtil.getDefaultLockLeasePeriod(); + ReturnType lockAcqResult = MusicCore.acquireLockWithLease(lockname, lockId, leasePeriod); + long lockAcqTime = System.currentTimeMillis(); + long zkPutTime = 0, lockReleaseTime = 0; + + if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { + logger.info("acquired lock with id " + lockId); + MusicCore.pureZkWrite(lockname, data); + zkPutTime = System.currentTimeMillis(); + boolean voluntaryRelease = true; + if (consistency.equals("atomic")) + MusicCore.releaseLock(lockId, voluntaryRelease); + else if (consistency.equals("atomic_delete_lock")) + MusicCore.deleteLock(lockname); + lockReleaseTime = System.currentTimeMillis(); + } else { + MusicCore.destroyLockRef(lockId); + } + + long actualUpdateCompletionTime = System.currentTimeMillis(); + + + long endTime = System.currentTimeMillis(); + + String lockingInfo = "|lock creation time:" + (lockCreationTime - jsonParseCompletionTime) + + "|lock accquire time:" + (lockAcqTime - lockCreationTime) + + "|zk put time:" + (zkPutTime - lockAcqTime); + + if (consistency.equals("atomic")) + lockingInfo = lockingInfo + "|lock release time:" + (lockReleaseTime - zkPutTime) + "|"; + else if (consistency.equals("atomic_delete_lock")) + lockingInfo = lockingInfo + "|lock delete time:" + (lockReleaseTime - zkPutTime) + "|"; + + String timingString = "Time taken in ms for Zookeeper " + consistency + " update-" + + operationId + ":" + "|total operation time:" + (endTime - startTime) + + "|json parsing time:" + (jsonParseCompletionTime - startTime) + + "|update time:" + (actualUpdateCompletionTime - jsonParseCompletionTime) + + lockingInfo; + + logger.info(timingString); + } + + /** + * + * @param insObj + * @param lockName + * @param nodeName + * @throws Exception + */ + @GET + @Path("/purezk/atomic/{lockname}/{name}") + @Consumes(MediaType.APPLICATION_JSON) + public void pureZkAtomicGet(JsonInsert insObj, @PathParam("lockname") String lockName, + @PathParam("name") String nodeName) throws Exception { + logger.info("--------------Zk atomic read-------------------------"); + long start = System.currentTimeMillis(); + String lockId = MusicCore.createLockReference(lockName); + long leasePeriod = MusicUtil.getDefaultLockLeasePeriod(); + ReturnType lockAcqResult = MusicCore.acquireLockWithLease(lockName, lockId, leasePeriod); + if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { + logger.info("acquired lock with id " + lockId); + MusicCore.pureZkRead(nodeName); + boolean voluntaryRelease = true; + MusicCore.releaseLock(lockId, voluntaryRelease); + } else { + MusicCore.destroyLockRef(lockId); + } + + long end = System.currentTimeMillis(); + logger.info("Total time taken for Zk atomic read:" + (end - start) + " ms"); + } + + /** + * + * doing an update directly to cassa but through the rest api + * + * @param insObj + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @PUT + @Path("/cassa/keyspaces/{keyspace}/tables/{tablename}/rows") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public boolean updateTableCassa(JsonInsert insObj, @PathParam("keyspace") String keyspace, + @PathParam("tablename") String tablename, @Context UriInfo info) + throws Exception { + long startTime = System.currentTimeMillis(); + String operationId = UUID.randomUUID().toString();// just for debugging purposes. + String consistency = insObj.getConsistencyInfo().get("type"); + logger.info("--------------Cassandra " + consistency + " update-" + operationId + + "-------------------------"); + PreparedQueryObject queryObject = new PreparedQueryObject(); + Map<String, Object> valuesMap = insObj.getValues(); + TableMetadata tableInfo = MusicCore.returnColumnMetadata(keyspace, tablename); + String vectorTs = "'" + Thread.currentThread().getId() + System.currentTimeMillis() + "'"; + String fieldValueString = "vector_ts= ? ,"; + queryObject.addValue(vectorTs); + + int counter = 0; + for (Map.Entry<String, Object> entry : valuesMap.entrySet()) { + Object valueObj = entry.getValue(); + DataType colType = tableInfo.getColumn(entry.getKey()).getType(); + Object valueString = MusicUtil.convertToActualDataType(colType, valueObj); + fieldValueString = fieldValueString + entry.getKey() + "= ?"; + queryObject.addValue(valueString); + if (counter != valuesMap.size() - 1) + fieldValueString = fieldValueString + ","; + counter = counter + 1; + } + + // get the row specifier + String rowSpec = ""; + counter = 0; + queryObject.appendQueryString("UPDATE " + keyspace + "." + tablename + " "); + MultivaluedMap<String, String> rowParams = info.getQueryParameters(); + String primaryKey = ""; + for (MultivaluedMap.Entry<String, List<String>> entry : rowParams.entrySet()) { + String keyName = entry.getKey(); + List<String> valueList = entry.getValue(); + String indValue = valueList.get(0); + DataType colType = tableInfo.getColumn(entry.getKey()).getType(); + Object formattedValue = MusicUtil.convertToActualDataType(colType, indValue); + primaryKey = primaryKey + indValue; + rowSpec = rowSpec + keyName + "= ? "; + queryObject.addValue(formattedValue); + if (counter != rowParams.size() - 1) + rowSpec = rowSpec + " AND "; + counter = counter + 1; + } + + + String ttl = insObj.getTtl(); + String timestamp = insObj.getTimestamp(); + + if ((ttl != null) && (timestamp != null)) { + + logger.info("both there"); + queryObject.appendQueryString(" USING TTL ? AND TIMESTAMP ?"); + queryObject.addValue(Integer.parseInt(ttl)); + queryObject.addValue(Long.parseLong(timestamp)); + } + + if ((ttl != null) && (timestamp == null)) { + logger.info("ONLY TTL there"); + queryObject.appendQueryString(" USING TTL ?"); + queryObject.addValue(Integer.parseInt(ttl)); + } + + if ((ttl == null) && (timestamp != null)) { + logger.info("ONLY timestamp there"); + queryObject.appendQueryString(" USING TIMESTAMP ?"); + queryObject.addValue(Long.parseLong(timestamp)); + } + queryObject.appendQueryString(" SET " + fieldValueString + " WHERE " + rowSpec + ";"); + + long jsonParseCompletionTime = System.currentTimeMillis(); + + boolean operationResult = true; + MusicCore.getDSHandle().executePut(queryObject, insObj.getConsistencyInfo().get("type")); + + long actualUpdateCompletionTime = System.currentTimeMillis(); + + long endTime = System.currentTimeMillis(); + + String timingString = "Time taken in ms for Cassandra " + consistency + " update-" + + operationId + ":" + "|total operation time:" + (endTime - startTime) + + "|json parsing time:" + (jsonParseCompletionTime - startTime) + + "|update time:" + (actualUpdateCompletionTime - jsonParseCompletionTime) + + "|"; + logger.info(timingString); + + return operationResult; + } + +} diff --git a/src/main/java/org/onap/music/rest/RestMusicDataAPI.java b/src/main/java/org/onap/music/rest/RestMusicDataAPI.java new file mode 100755 index 00000000..ba0f1a3b --- /dev/null +++ b/src/main/java/org/onap/music/rest/RestMusicDataAPI.java @@ -0,0 +1,1088 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.rest; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +// import java.util.logging.Level; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +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.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriInfo; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.onap.music.datastore.PreparedQueryObject; +import org.onap.music.datastore.jsonobjects.JsonDelete; +import org.onap.music.datastore.jsonobjects.JsonInsert; +import org.onap.music.datastore.jsonobjects.JsonKeySpace; +import org.onap.music.response.jsonobjects.JsonResponse; +import org.onap.music.datastore.jsonobjects.JsonTable; +import org.onap.music.datastore.jsonobjects.JsonUpdate; +import org.onap.music.main.CachingUtil; +import org.onap.music.main.MusicCore; +import org.onap.music.main.MusicUtil; +import org.onap.music.main.ResultType; +import org.onap.music.main.ReturnType; +import org.onap.music.main.MusicCore.Condition; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.TableMetadata; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; + +@Path("/v{version: [0-9]+}/keyspaces") +@Api(value = "Data Api") +public class RestMusicDataAPI { + /* + * Header values for Versioning X-minorVersion *** - Used to request or communicate a MINOR + * version back from the client to the server, and from the server back to the client - This + * will be the MINOR version requested by the client, or the MINOR version of the last MAJOR + * version (if not specified by the client on the request) - Contains a single position value + * (e.g. if the full version is 1.24.5, X-minorVersion = "24") - Is optional for the client on + * request; however, this header should be provided if the client needs to take advantage of + * MINOR incremented version functionality - Is mandatory for the server on response + * + *** X-patchVersion *** - Used only to communicate a PATCH version in a response for + * troubleshooting purposes only, and will not be provided by the client on request - This will + * be the latest PATCH version of the MINOR requested by the client, or the latest PATCH version + * of the MAJOR (if not specified by the client on the request) - Contains a single position + * value (e.g. if the full version is 1.24.5, X-patchVersion = "5") - Is mandatory for the + * server on response + * + *** X-latestVersion *** - Used only to communicate an API's latest version - Is mandatory for the + * server on response, and shall include the entire version of the API (e.g. if the full version + * is 1.24.5, X-latestVersion = "1.24.5") - Used in the response to inform clients that they are + * not using the latest version of the API + * + */ + + + private static EELFLogger logger = EELFManager.getInstance().getLogger(RestMusicDataAPI.class); + private static String xLatestVersion = "X-latestVersion"; + + private class RowIdentifier { + public String primarKeyValue; + public StringBuilder rowIdString; + public PreparedQueryObject queryObject;// the string with all the row + // identifiers separated by AND + + public RowIdentifier(String primaryKeyValue, StringBuilder rowIdString, + PreparedQueryObject queryObject) { + this.primarKeyValue = primaryKeyValue; + this.rowIdString = rowIdString; + this.queryObject = queryObject; + } + } + + private String buildVersion(String major, String minor, String patch) { + if (minor != null) { + major += "." + minor; + if (patch != null) { + major += "." + patch; + } + } + return major; + } + + /** + * Create Keyspace REST + * + * @param kspObject + * @param keyspaceName + * @return + * @throws Exception + */ + @POST + @Path("/{name}") + @ApiOperation(value = "Create Keyspace", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map<String, Object> createKeySpace( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + JsonKeySpace kspObject, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("name") String keyspaceName, + @Context HttpServletResponse response) throws Exception { + Map<String, Object> resultMap = CachingUtil.verifyOnboarding(ns, userId, password); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (!resultMap.isEmpty()) { + return resultMap; + } + + resultMap = MusicCore.autheticateUser(ns, userId, password, keyspaceName, aid, + "createKeySpace"); + String newAid = null; + if (!resultMap.isEmpty()) { + if (resultMap.containsKey("aid")) { + newAid = (String) resultMap.get("aid"); + } else + return resultMap; + } + + String consistency = MusicUtil.EVENTUAL;// for now this needs only eventual + // consistency + + PreparedQueryObject queryObject = new PreparedQueryObject(); + boolean result = false; + long start = System.currentTimeMillis(); + Map<String, Object> replicationInfo = kspObject.getReplicationInfo(); + String repString = "{" + MusicUtil.jsonMaptoSqlString(replicationInfo, ",") + "}"; + queryObject.appendQueryString( + "CREATE KEYSPACE " + keyspaceName + " WITH replication = " + repString); + if (kspObject.getDurabilityOfWrites() != null) { + queryObject.appendQueryString( + " AND durable_writes = " + kspObject.getDurabilityOfWrites()); + } + + queryObject.appendQueryString(";"); + long end = System.currentTimeMillis(); + logger.info("Time taken for setting up query in create keyspace:" + (end - start)); + + try { + result = MusicCore.nonKeyRelatedPut(queryObject, consistency); + logger.debug("resulta = " + result); + } catch (Exception e) { + logger.error(e.getMessage()); + + } + logger.debug("result = " + result); + if (!result) { + resultMap.put("Status", String.valueOf(result)); + resultMap.put("Exception", "Keyspace already exists. Please contact admin."); + if (resultMap.get("uuid").equals("new")) { + queryObject = new PreparedQueryObject(); + queryObject.appendQueryString( + "DELETE FROM admin.keyspace_master where uuid = " + newAid); + queryObject.appendQueryString(";"); + result = MusicCore.nonKeyRelatedPut(queryObject, consistency); + resultMap.remove("aid"); + resultMap.remove("uuid"); + return resultMap; + + } else { + queryObject = new PreparedQueryObject(); + queryObject.appendQueryString( + "UPDATE admin.keyspace_master SET keyspace_name=?,password=?,is_api=null where uuid = ?;"); + queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), + MusicUtil.DEFAULTKEYSPACENAME)); + queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), null)); + queryObject.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), newAid)); + result = MusicCore.nonKeyRelatedPut(queryObject, consistency); + resultMap.remove("aid"); + resultMap.remove("uuid"); + return resultMap; + } + + } + try { + queryObject = new PreparedQueryObject(); + queryObject.appendQueryString("CREATE ROLE IF NOT EXISTS '" + userId + + "' WITH PASSWORD = '" + password + "' AND LOGIN = true;"); + result = MusicCore.nonKeyRelatedPut(queryObject, consistency); + if (result) { + queryObject = new PreparedQueryObject(); + queryObject.appendQueryString("GRANT ALL PERMISSIONS on KEYSPACE " + keyspaceName + + " to " + userId); + queryObject.appendQueryString(";"); + result = MusicCore.nonKeyRelatedPut(queryObject, consistency); + } else { + resultMap.remove("uuid"); + resultMap.put("Exception", "Exception while creating user."); + return resultMap; + } + } catch (Exception e) { + logger.error(e.getMessage()); + } + resultMap.remove("uuid"); + if (CachingUtil.isAAFApplication(ns)) + resultMap.remove("aid"); + resultMap.put("Status", String.valueOf(result)); + return resultMap; + + } + + /** + * + * @param kspObject + * @param keyspaceName + * @return + * @throws Exception + */ + @DELETE + @Path("/{name}") + @ApiOperation(value = "Delete Keyspace", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map<String, Object> dropKeySpace( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + JsonKeySpace kspObject, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("name") String keyspaceName, + @Context HttpServletResponse response) throws Exception { + Map<String, Object> resultMap = MusicCore.autheticateUser(ns, userId, password, + keyspaceName, aid, "dropKeySpace"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) { + return resultMap; + } + + String consistency = MusicUtil.EVENTUAL;// for now this needs only eventual + // consistency + String appName = CachingUtil.getAppName(keyspaceName); + String uuid = CachingUtil.getUuidFromMusicCache(keyspaceName); + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "select count(*) as count from admin.keyspace_master where application_name=? allow filtering;"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName)); + Row row = MusicCore.get(pQuery).one(); + long count = row.getLong(0); + + if (count == 0) { + resultMap.put("Exception", "Keyspace not found. Please make sure keyspace exists."); + return resultMap; + } else if (count == 1) { + pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "UPDATE admin.keyspace_master SET keyspace_name=?,password=?,is_api=null where uuid = ?;"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), + MusicUtil.DEFAULTKEYSPACENAME)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), null)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), uuid)); + MusicCore.nonKeyRelatedPut(pQuery, consistency); + } else { + pQuery = new PreparedQueryObject(); + pQuery.appendQueryString("delete from admin.keyspace_master where uuid = ?"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), uuid)); + MusicCore.nonKeyRelatedPut(pQuery, consistency); + } + + PreparedQueryObject queryObject = new PreparedQueryObject(); + queryObject.appendQueryString("DROP KEYSPACE " + keyspaceName + ";"); + return new JsonResponse(MusicCore.nonKeyRelatedPut(queryObject, consistency), "", "") + .toMap(); + } + + /** + * + * @param tableObj + * @param keyspace + * @param tablename + * @return + * @throws Exception + */ + @POST + @Path("/{keyspace}/tables/{tablename}") + @ApiOperation(value = "Create Table", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map<String, Object> createTable( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + JsonTable tableObj, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @Context HttpServletResponse response) throws Exception { + Map<String, Object> resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace, + aid, "createTable"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) { + return resultMap; + } + String consistency = MusicUtil.EVENTUAL; + // for now this needs only eventual consistency + PreparedQueryObject queryObject = new PreparedQueryObject(); + boolean result = false; + // first read the information about the table fields + Map<String, String> fields = tableObj.getFields(); + StringBuilder fieldsString = new StringBuilder("(vector_ts text,"); + int counter = 0; + String primaryKey; + for (Map.Entry<String, String> entry : fields.entrySet()) { + fieldsString.append("" + entry.getKey() + " " + entry.getValue() + ""); + if (entry.getKey().equals("PRIMARY KEY")) { + primaryKey = entry.getValue().substring(entry.getValue().indexOf('(') + 1); + primaryKey = primaryKey.substring(0, primaryKey.indexOf(')')); + } + if (counter == fields.size() - 1) + fieldsString.append(")"); + else + fieldsString.append(","); + counter = counter + 1; + } + // information about the name-value style properties + Map<String, Object> propertiesMap = tableObj.getProperties(); + StringBuilder propertiesString = new StringBuilder(); + if (propertiesMap != null) { + counter = 0; + for (Map.Entry<String, Object> entry : propertiesMap.entrySet()) { + Object ot = entry.getValue(); + String value = ot + ""; + if (ot instanceof String) { + value = "'" + value + "'"; + } else if (ot instanceof Map) { + Map<String, Object> otMap = (Map<String, Object>) ot; + value = "{" + MusicUtil.jsonMaptoSqlString(otMap, ",") + "}"; + } + + propertiesString.append(entry.getKey() + "=" + value + ""); + if (counter != propertiesMap.size() - 1) + propertiesString.append(" AND "); + + counter = counter + 1; + } + } + + queryObject.appendQueryString("CREATE TABLE IF NOT EXISTS " + keyspace + "." + tablename + + " " + fieldsString); + + if (propertiesMap != null) + queryObject.appendQueryString(" WITH " + propertiesString); + + queryObject.appendQueryString(";"); + result = MusicCore.nonKeyRelatedPut(queryObject, consistency); + + return new JsonResponse(result, "", "").toMap(); + } + + /** + * + * @param keyspace + * @param tablename + * @param fieldName + * @param info + * @throws Exception + */ + @POST + @Path("/{keyspace}/tables/{tablename}/index/{field}") + @ApiOperation(value = "Create Index", response = String.class) + @Produces(MediaType.APPLICATION_JSON) + public Map<String, Object> createIndex( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @ApiParam(value = "Field Name", + required = true) @PathParam("field") String fieldName, + @Context UriInfo info, @Context HttpServletResponse response) throws Exception { + Map<String, Object> resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace, + aid, "createIndex"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) + return resultMap; + MultivaluedMap<String, String> rowParams = info.getQueryParameters(); + String indexName = ""; + if (rowParams.getFirst("index_name") != null) + indexName = rowParams.getFirst("index_name"); + PreparedQueryObject query = new PreparedQueryObject(); + query.appendQueryString("Create index " + indexName + " if not exists on " + keyspace + "." + + tablename + " (" + fieldName + ");"); + return new JsonResponse(MusicCore.nonKeyRelatedPut(query, "eventual"), "", "").toMap(); + + } + + /** + * + * @param insObj + * @param keyspace + * @param tablename + * @return + * @throws Exception + */ + @POST + @Path("/{keyspace}/tables/{tablename}/rows") + @ApiOperation(value = "Insert Into Table", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map<String, Object> insertIntoTable( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + JsonInsert insObj, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @Context HttpServletResponse response) throws Exception { + Map<String, Object> resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace, + aid, "insertIntoTable"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) { + return resultMap; + } + ReturnType result = null; + Map<String, Object> valuesMap = insObj.getValues(); + PreparedQueryObject queryObject = new PreparedQueryObject(); + TableMetadata tableInfo = MusicCore.returnColumnMetadata(keyspace, tablename); + String primaryKeyName = tableInfo.getPrimaryKey().get(0).getName(); + StringBuilder fieldsString = new StringBuilder("(vector_ts,"); + String vectorTs = + String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis()); + StringBuilder valueString = new StringBuilder("(" + "?" + ","); + queryObject.addValue(vectorTs); + int counter = 0; + String primaryKey = ""; + + for (Map.Entry<String, Object> entry : valuesMap.entrySet()) { + fieldsString.append("" + entry.getKey()); + Object valueObj = entry.getValue(); + if (primaryKeyName.equals(entry.getKey())) { + primaryKey = entry.getValue() + ""; + primaryKey = primaryKey.replace("'", "''"); + } + + DataType colType = tableInfo.getColumn(entry.getKey()).getType(); + + Object formattedValue = MusicUtil.convertToActualDataType(colType, valueObj); + valueString.append("?"); + queryObject.addValue(formattedValue); + + if (counter == valuesMap.size() - 1) { + fieldsString.append(")"); + valueString.append(")"); + } else { + fieldsString.append(","); + valueString.append(","); + } + counter = counter + 1; + } + + queryObject.appendQueryString("INSERT INTO " + keyspace + "." + tablename + " " + + fieldsString + " VALUES " + valueString); + + String ttl = insObj.getTtl(); + String timestamp = insObj.getTimestamp(); + + if ((ttl != null) && (timestamp != null)) { + logger.info("both there"); + queryObject.appendQueryString(" USING TTL ? AND TIMESTAMP ?"); + queryObject.addValue(Integer.parseInt(ttl)); + queryObject.addValue(Long.parseLong(timestamp)); + } + + if ((ttl != null) && (timestamp == null)) { + logger.info("ONLY TTL there"); + queryObject.appendQueryString(" USING TTL ?"); + queryObject.addValue(Integer.parseInt(ttl)); + } + + if ((ttl == null) && (timestamp != null)) { + logger.info("ONLY timestamp there"); + queryObject.appendQueryString(" USING TIMESTAMP ?"); + queryObject.addValue(Long.parseLong(timestamp)); + } + + queryObject.appendQueryString(";"); + + String consistency = insObj.getConsistencyInfo().get("type"); + try { + if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL)) { + result = MusicCore.eventualPut(queryObject); + } else if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) { + String lockId = insObj.getConsistencyInfo().get("lockId"); + result = MusicCore.criticalPut(keyspace, tablename, primaryKey, queryObject, lockId, + null); + } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) { + result = MusicCore.atomicPut(keyspace, tablename, primaryKey, queryObject, null); + + } + return (result != null) ? result.toMap() + : new ReturnType(ResultType.FAILURE, + "Null result - Please Contact admin").toMap(); + } catch (Exception ex) { + logger.error(ex.getMessage()); + return new ReturnType(ResultType.FAILURE, ex.getMessage()).toMap(); + } + } + + /** + * + * @param insObj + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @PUT + @Path("/{keyspace}/tables/{tablename}/rows") + @ApiOperation(value = "Update Table", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map<String, Object> updateTable( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + JsonUpdate updateObj, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @Context UriInfo info, @Context HttpServletResponse response) throws Exception { + Map<String, Object> resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace, + aid, "updateTable"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) { + return resultMap; + } + long startTime = System.currentTimeMillis(); + String operationId = UUID.randomUUID().toString();// just for infoging + // purposes. + String consistency = updateObj.getConsistencyInfo().get("type"); + logger.info("--------------Music " + consistency + " update-" + operationId + + "-------------------------"); + // obtain the field value pairs of the update + + PreparedQueryObject queryObject = new PreparedQueryObject(); + Map<String, Object> valuesMap = updateObj.getValues(); + + TableMetadata tableInfo = MusicCore.returnColumnMetadata(keyspace, tablename); + String vectorTs = + String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis()); + StringBuilder fieldValueString = new StringBuilder("vector_ts=?,"); + queryObject.addValue(vectorTs); + int counter = 0; + for (Map.Entry<String, Object> entry : valuesMap.entrySet()) { + Object valueObj = entry.getValue(); + DataType colType = tableInfo.getColumn(entry.getKey()).getType(); + Object valueString = MusicUtil.convertToActualDataType(colType, valueObj); + fieldValueString.append(entry.getKey() + "= ?"); + queryObject.addValue(valueString); + if (counter != valuesMap.size() - 1) + fieldValueString.append(","); + counter = counter + 1; + } + String ttl = updateObj.getTtl(); + String timestamp = updateObj.getTimestamp(); + + queryObject.appendQueryString("UPDATE " + keyspace + "." + tablename + " "); + if ((ttl != null) && (timestamp != null)) { + + logger.info("both there"); + queryObject.appendQueryString(" USING TTL ? AND TIMESTAMP ?"); + queryObject.addValue(Integer.parseInt(ttl)); + queryObject.addValue(Long.parseLong(timestamp)); + } + + if ((ttl != null) && (timestamp == null)) { + logger.info("ONLY TTL there"); + queryObject.appendQueryString(" USING TTL ?"); + queryObject.addValue(Integer.parseInt(ttl)); + } + + if ((ttl == null) && (timestamp != null)) { + logger.info("ONLY timestamp there"); + queryObject.appendQueryString(" USING TIMESTAMP ?"); + queryObject.addValue(Long.parseLong(timestamp)); + } + // get the row specifier + RowIdentifier rowId = getRowIdentifier(keyspace, tablename, info.getQueryParameters(), + queryObject); + + queryObject.appendQueryString( + " SET " + fieldValueString + " WHERE " + rowId.rowIdString + ";"); + + // get the conditional, if any + Condition conditionInfo; + if (updateObj.getConditions() == null) + conditionInfo = null; + else {// to avoid parsing repeatedly, just send the select query to + // obtain row + String selectQuery = "SELECT * FROM " + keyspace + "." + tablename + " WHERE " + + rowId.rowIdString + ";"; + conditionInfo = new MusicCore.Condition(updateObj.getConditions(), selectQuery); + } + + ReturnType operationResult = null; + long jsonParseCompletionTime = System.currentTimeMillis(); + + if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL)) + operationResult = MusicCore.eventualPut(queryObject); + else if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) { + String lockId = updateObj.getConsistencyInfo().get("lockId"); + operationResult = MusicCore.criticalPut(keyspace, tablename, rowId.primarKeyValue, + queryObject, lockId, conditionInfo); + } + else if (consistency.equalsIgnoreCase("atomic_delete_lock")) { + // this function is mainly for the benchmarks + operationResult = MusicCore.atomicPutWithDeleteLock(keyspace, tablename, + rowId.primarKeyValue, queryObject, conditionInfo); + } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) { + operationResult = MusicCore.atomicPut(keyspace, tablename, rowId.primarKeyValue, + queryObject, conditionInfo); + } + long actualUpdateCompletionTime = System.currentTimeMillis(); + + long endTime = System.currentTimeMillis(); + String timingString = "Time taken in ms for Music " + consistency + " update-" + operationId + + ":" + "|total operation time:" + (endTime - startTime) + + "|json parsing time:" + (jsonParseCompletionTime - startTime) + + "|update time:" + (actualUpdateCompletionTime - jsonParseCompletionTime) + + "|"; + + if (operationResult != null && operationResult.getTimingInfo() != null) { + String lockManagementTime = operationResult.getTimingInfo(); + timingString = timingString + lockManagementTime; + } + logger.info(timingString); + return (operationResult != null) ? operationResult.toMap() + : new ReturnType(ResultType.FAILURE, "Null result - Please Contact admin") + .toMap(); + } + + /** + * + * @param delObj + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @DELETE + @Path("/{keyspace}/tables/{tablename}/rows") + @ApiOperation(value = "Delete From table", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map<String, Object> deleteFromTable( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + JsonDelete delObj, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @Context UriInfo info, @Context HttpServletResponse response) throws Exception { + Map<String, Object> resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace, + aid, "deleteFromTable"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) { + return resultMap; + } + PreparedQueryObject queryObject = new PreparedQueryObject(); + StringBuilder columnString = new StringBuilder(); + + int counter = 0; + ArrayList<String> columnList = delObj.getColumns(); + if (columnList != null) { + for (String column : columnList) { + columnString.append(column); + if (counter != columnList.size() - 1) + columnString.append(","); + counter = counter + 1; + } + } + + // get the row specifier + RowIdentifier rowId = getRowIdentifier(keyspace, tablename, info.getQueryParameters(), + queryObject); + String rowSpec = rowId.rowIdString.toString(); + + if ((columnList != null) && (!rowSpec.isEmpty())) { + queryObject.appendQueryString("DELETE " + columnString + " FROM " + keyspace + "." + + tablename + " WHERE " + rowSpec + ";"); + } + + if ((columnList == null) && (!rowSpec.isEmpty())) { + queryObject.appendQueryString("DELETE FROM " + keyspace + "." + tablename + " WHERE " + + rowSpec + ";"); + } + + if ((columnList != null) && (rowSpec.isEmpty())) { + queryObject.appendQueryString( + "DELETE " + columnString + " FROM " + keyspace + "." + rowSpec + ";"); + } + + // get the conditional, if any + Condition conditionInfo; + if (delObj.getConditions() == null) + conditionInfo = null; + else {// to avoid parsing repeatedly, just send the select query to + // obtain row + String selectQuery = "SELECT * FROM " + keyspace + "." + tablename + " WHERE " + + rowId.rowIdString + ";"; + conditionInfo = new MusicCore.Condition(delObj.getConditions(), selectQuery); + } + + String consistency = delObj.getConsistencyInfo().get("type"); + + ReturnType operationResult = null; + + if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL)) + operationResult = MusicCore.eventualPut(queryObject); + else if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) { + String lockId = delObj.getConsistencyInfo().get("lockId"); + operationResult = MusicCore.criticalPut(keyspace, tablename, rowId.primarKeyValue, + queryObject, lockId, conditionInfo); + } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) { + operationResult = MusicCore.atomicPut(keyspace, tablename, rowId.primarKeyValue, + queryObject, conditionInfo); + } + try { + return operationResult.toMap(); + } catch (NullPointerException e) { + return new ReturnType(ResultType.FAILURE, e.getMessage()).toMap(); + } + } + + /** + * + * @param tabObj + * @param keyspace + * @param tablename + * @throws Exception + */ + @DELETE + @Path("/{keyspace}/tables/{tablename}") + @ApiOperation(value = "Drop Table", response = String.class) + + public Map<String, Object> dropTable( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + JsonTable tabObj, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @Context HttpServletResponse response) throws Exception { + Map<String, Object> resultMap = + MusicCore.autheticateUser(ns, userId, password, keyspace, aid, "dropTable"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) { + return resultMap; + } + String consistency = "eventual";// for now this needs only eventual + // consistency + PreparedQueryObject query = new PreparedQueryObject(); + query.appendQueryString("DROP TABLE IF EXISTS " + keyspace + "." + tablename + ";"); + return new JsonResponse(MusicCore.nonKeyRelatedPut(query, consistency), "", "").toMap(); + } + + /** + * + * @param selObj + * @param keyspace + * @param tablename + * @param info + * @return + */ + @PUT + @Path("/{keyspace}/tables/{tablename}/rows/criticalget") + @ApiOperation(value = "Select Critical", response = Map.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map<String, HashMap<String, Object>> selectCritical( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + JsonInsert selObj, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @Context UriInfo info, @Context HttpServletResponse response) throws Exception { + Map<String, Object> resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace, + aid, "selectCritical"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) { + logger.error("Error while authentication... "); + HashMap<String, Object> tempMap = new HashMap<>(); + tempMap.putAll(resultMap); + Map<String, HashMap<String, Object>> results = new HashMap<>(); + results.put("Result", tempMap); + return results; + } + String lockId = selObj.getConsistencyInfo().get("lockId"); + + PreparedQueryObject queryObject = new PreparedQueryObject(); + StringBuilder rowSpec = new StringBuilder(); + + RowIdentifier rowId = getRowIdentifier(keyspace, tablename, info.getQueryParameters(), + queryObject); + + queryObject.appendQueryString( + "SELECT * FROM " + keyspace + "." + tablename + " WHERE " + rowSpec + ";"); + + ResultSet results = null; + + String consistency = selObj.getConsistencyInfo().get("type"); + + if (consistency.equalsIgnoreCase("critical")) { + results = MusicCore.criticalGet(keyspace, tablename, rowId.primarKeyValue, queryObject, + lockId); + } else if (consistency.equalsIgnoreCase("atomic")) { + results = MusicCore.atomicGet(keyspace, tablename, rowId.primarKeyValue, queryObject); + } + + return MusicCore.marshallResults(results); + } + + /** + * + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @GET + @Path("/{keyspace}/tables/{tablename}/rows") + @ApiOperation(value = "Select All or Select Specivic", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map<String, HashMap<String, Object>> select( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @Context UriInfo info, @Context HttpServletResponse response) throws Exception { + Map<String, Object> resultMap = + MusicCore.autheticateUser(ns, userId, password, keyspace, aid, "select"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) { + logger.error("Error while authentication... "); + HashMap<String, Object> tempMap = new HashMap<>(); + tempMap.putAll(resultMap); + Map<String, HashMap<String, Object>> results = new HashMap<>(); + results.put("Result", tempMap); + return results; + } + PreparedQueryObject queryObject = new PreparedQueryObject(); + + if (info.getQueryParameters().isEmpty())// select all + queryObject.appendQueryString("SELECT * FROM " + keyspace + "." + tablename + ";"); + else { + int limit = -1; // do not limit the number of results + queryObject = selectSpecificQuery(version, minorVersion, patchVersion, aid, ns, userId, + password, keyspace, tablename, info, limit); + } + ResultSet results = MusicCore.get(queryObject); + return MusicCore.marshallResults(results); + } + + /** + * + * @param keyspace + * @param tablename + * @param info + * @param limit + * @return + * @throws Exception + */ + public PreparedQueryObject selectSpecificQuery(String version, String minorVersion, + String patchVersion, String aid, String ns, String userId, String password, + String keyspace, String tablename, UriInfo info, int limit) { + + PreparedQueryObject queryObject = new PreparedQueryObject(); + StringBuilder rowIdString = getRowIdentifier(keyspace, tablename, info.getQueryParameters(), + queryObject).rowIdString; + + queryObject.appendQueryString( + "SELECT * FROM " + keyspace + "." + tablename + " WHERE " + rowIdString); + + if (limit != -1) { + queryObject.appendQueryString(" LIMIT " + limit); + } + + queryObject.appendQueryString(";"); + return queryObject; + + } + + /** + * + * @param keyspace + * @param tablename + * @param rowParams + * @param queryObject + * @return + * @throws Exception + */ + private RowIdentifier getRowIdentifier(String keyspace, String tablename, + MultivaluedMap<String, String> rowParams, PreparedQueryObject queryObject) { + StringBuilder rowSpec = new StringBuilder(); + int counter = 0; + TableMetadata tableInfo = MusicCore.returnColumnMetadata(keyspace, tablename); + StringBuilder primaryKey = new StringBuilder(); + for (MultivaluedMap.Entry<String, List<String>> entry : rowParams.entrySet()) { + String keyName = entry.getKey(); + List<String> valueList = entry.getValue(); + String indValue = valueList.get(0); + DataType colType = tableInfo.getColumn(entry.getKey()).getType(); + Object formattedValue = MusicUtil.convertToActualDataType(colType, indValue); + primaryKey.append(indValue); + rowSpec.append(keyName + "= ?"); + queryObject.addValue(formattedValue); + if (counter != rowParams.size() - 1) + rowSpec.append(" AND "); + counter = counter + 1; + } + return new RowIdentifier(primaryKey.toString(), rowSpec, queryObject); + } + +} diff --git a/src/main/java/org/onap/music/rest/RestMusicLocksAPI.java b/src/main/java/org/onap/music/rest/RestMusicLocksAPI.java new file mode 100644 index 00000000..8612b1fa --- /dev/null +++ b/src/main/java/org/onap/music/rest/RestMusicLocksAPI.java @@ -0,0 +1,209 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.rest; + +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +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.MediaType; + +import org.onap.music.datastore.jsonobjects.JsonLeasedLock; +import org.onap.music.lockingservice.MusicLockState; +import org.onap.music.main.MusicCore; +import org.onap.music.main.MusicUtil; +import org.onap.music.response.jsonobjects.JsonLockResponse; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; + + +@Path("/v{version: [0-9]+}/locks/") +@Api(value="Lock Api") +public class RestMusicLocksAPI { + + private static EELFLogger logger = EELFManager.getInstance().getLogger(RestMusicLocksAPI.class); + private static String xLatestVersion = "X-latestVersion"; + /** + * Puts the requesting process in the q for this lock. The corresponding + * node will be created in zookeeper if it did not already exist + * + * @param lockName + * @return + */ + + @POST + @Path("/create/{lockname}") + @ApiOperation(value = "Create Lock", + notes = "Puts the requesting process in the q for this lock." + + " The corresponding node will be created in zookeeper if it did not already exist." + + " Lock Name is the \"key\" of the form keyspaceName.tableName.rowId", + response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map<String,Object> createLockReference( + @ApiParam(value="Lock Name",required=true) @PathParam("lockname") String lockName, + @Context HttpServletResponse response){ + response.addHeader(xLatestVersion,MusicUtil.getVersion()); + Boolean status = true; + String lockId = MusicCore.createLockReference(lockName); + if ( lockId == null ) { status = false; } + return new JsonLockResponse(status.toString(),"",lockId).toMap(); + } + + /** + * + * Checks if the node is in the top of the queue and hence acquires the lock + * + * @param lockId + * @return + */ + @GET + @Path("/acquire/{lockreference}") + @ApiOperation(value = "Aquire Lock", + notes = "Checks if the node is in the top of the queue and hence acquires the lock", + response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map<String,Object> accquireLock( + @ApiParam(value="Lock Reference",required=true) @PathParam("lockreference") String lockId, + @Context HttpServletResponse response){ + response.addHeader(xLatestVersion,MusicUtil.getVersion()); + String lockName = lockId.substring(lockId.indexOf('$')+1, lockId.lastIndexOf('$')); + Boolean lockStatus = MusicCore.acquireLock(lockName,lockId); + return new JsonLockResponse(lockStatus.toString(),"",lockId,lockStatus.toString(),"").toMap(); + } + + + + + @POST + @Path("/acquire-with-lease/{lockreference}") + @ApiOperation(value = "Aquire Lock with Lease", response = Map.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map<String,Object> accquireLockWithLease(JsonLeasedLock lockObj, + @ApiParam(value="Lock Reference",required=true) @PathParam("lockreference") String lockId, + @Context HttpServletResponse response){ + response.addHeader(xLatestVersion,MusicUtil.getVersion()); + String lockName = lockId.substring(lockId.indexOf('$')+1, lockId.lastIndexOf('$')); + String lockLeaseStatus = MusicCore.acquireLockWithLease(lockName, lockId, lockObj.getLeasePeriod()).toString(); + return new JsonLockResponse(lockLeaseStatus,"",lockName,lockLeaseStatus,"",String.valueOf(lockObj.getLeasePeriod())).toMap(); + } + + + @GET + @Path("/enquire/{lockname}") + @ApiOperation(value = "Get Lock Holder", + notes = "Gets the current Lock Holder", + response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map<String,Object> currentLockHolder( + @ApiParam(value="Lock Name",required=true) @PathParam("lockname") String lockName, + @Context HttpServletResponse response){ + response.addHeader(xLatestVersion,MusicUtil.getVersion()); + String who = MusicCore.whoseTurnIsIt(lockName); + String status = "true"; + String error = ""; + if ( who == null ) { + status = "false"; + error = "There was a problem getting the lock holder"; + } + return new JsonLockResponse(status,error,lockName,"",who).toMap(); + } + + @GET + @Path("/{lockname}") + @ApiOperation(value = "Lock State", + notes = "Returns current Lock State and Holder.", + response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map<String,Object> currentLockState( + @ApiParam(value="Lock Name",required=true) @PathParam("lockname") String lockName, + @Context HttpServletResponse response){ + response.addHeader(xLatestVersion,MusicUtil.getVersion()); + MusicLockState mls = MusicCore.getMusicLockState(lockName); + Map<String,Object> returnMap = null; + JsonLockResponse jsonResponse = new JsonLockResponse("false","",lockName); + if(mls == null) { + jsonResponse.setError(""); + jsonResponse.setMessage("No lock object created yet.."); + } else { + jsonResponse.setStatus("true"); + jsonResponse.setLockStatus(mls.getLockStatus().toString()); + jsonResponse.setLockHolder(mls.getLockHolder()); + } + return returnMap; + } + + /** + * + * deletes the process from the zk queue + * + * @param lockId + */ + @DELETE + @Path("/release/{lockreference}") + @ApiOperation(value = "Release Lock", + notes = "deletes the process from the zk queue", + response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map<String,Object> unLock(@PathParam("lockreference") String lockId, + @Context HttpServletResponse response){ + response.addHeader(xLatestVersion,MusicUtil.getVersion()); + boolean voluntaryRelease = true; + MusicLockState mls = MusicCore.releaseLock(lockId,voluntaryRelease); + Map<String,Object> returnMap = null; + if ( mls.getLockStatus() == MusicLockState.LockStatus.UNLOCKED ) { + returnMap = new JsonLockResponse("Unlocked","","").toMap(); + } + if ( mls.getLockStatus() == MusicLockState.LockStatus.LOCKED) { + returnMap = new JsonLockResponse("Locked","","").toMap(); + } + return returnMap; + } + + /** + * + * @param lockName + */ + @DELETE + @Path("/delete/{lockname}") + @ApiOperation(value = "Delete Lock", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map<String,Object> deleteLock(@PathParam("lockname") String lockName, + @Context HttpServletResponse response){ + response.addHeader(xLatestVersion,MusicUtil.getVersion()); + MusicCore.deleteLock(lockName); + return new JsonLockResponse("true","","").toMap(); + } + +} diff --git a/src/main/java/org/onap/music/rest/RestMusicQAPI.java b/src/main/java/org/onap/music/rest/RestMusicQAPI.java new file mode 100755 index 00000000..3e92461c --- /dev/null +++ b/src/main/java/org/onap/music/rest/RestMusicQAPI.java @@ -0,0 +1,257 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.rest; + + + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +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.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.onap.music.datastore.jsonobjects.JsonDelete; +import org.onap.music.datastore.jsonobjects.JsonInsert; +import org.onap.music.datastore.jsonobjects.JsonTable; +import org.onap.music.datastore.jsonobjects.JsonUpdate; +import org.onap.music.main.MusicCore; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.onap.music.datastore.PreparedQueryObject; +import com.datastax.driver.core.ResultSet; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; + +//@Path("/v{version: [0-9]+}/priorityq/") +@Path("/priorityq/") +@Api(value="Q Api") +public class RestMusicQAPI { + + private static EELFLogger logger = EELFManager.getInstance().getLogger(RestMusicQAPI.class); + + + /** + * + * @param tableObj + * @param keyspace + * @param tablename + * @throws Exception + */ + + @POST + @Path("/keyspaces/{keyspace}/{qname}") + @ApiOperation(value = "", response = Void.class) + @Consumes(MediaType.APPLICATION_JSON) + public Map<String,Object> createQ( + @ApiParam(value="Major Version",required=true) @PathParam("version") String version, + @ApiParam(value="Minor Version",required=false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value="Patch Version",required=false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value="AID",required=true) @HeaderParam("aid") String aid, + @ApiParam(value="Application namespace",required=true) @HeaderParam("ns") String ns, + @ApiParam(value="userId",required=true) @HeaderParam("userId") String userId, + @ApiParam(value="Password",required=true) @HeaderParam("password") String password, JsonTable tableObj, + @ApiParam(value="Key Space",required=true) @PathParam("keyspace") String keyspace, + @ApiParam(value="Table Name",required=true) @PathParam("tablename") String tablename, + @Context HttpServletResponse response) throws Exception{ + return new RestMusicDataAPI().createTable(version,minorVersion,patchVersion,aid, ns, userId, password, tableObj, keyspace, tablename,response); + } + + /** + * + * @param insObj + * @param keyspace + * @param tablename + * @throws Exception + */ + @POST + @Path("/keyspaces/{keyspace}/{qname}/rows") + @ApiOperation(value = "", response = Void.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map<String,Object> insertIntoQ( + @ApiParam(value="Major Version",required=true) @PathParam("version") String version, + @ApiParam(value="Minor Version",required=false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value="Patch Version",required=false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value="AID",required=true) @HeaderParam("aid") String aid, + @ApiParam(value="Application namespace",required=true) @HeaderParam("ns") String ns, @ApiParam(value="userId",required=true) @HeaderParam("userId") String userId, + @ApiParam(value="Password",required=true) @HeaderParam("password") String password, JsonInsert insObj, + @ApiParam(value="Key Space",required=true) @PathParam("keyspace") String keyspace, + @ApiParam(value="Table Name",required=true) @PathParam("tablename") String tablename, + @Context HttpServletResponse response) throws Exception{ + return new RestMusicDataAPI().insertIntoTable(version,minorVersion,patchVersion,aid, ns, userId, password, insObj, keyspace, tablename,response); + } + + /** + * + * @param updateObj + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @PUT + @Path("/keyspaces/{keyspace}/{qname}/rows") + @ApiOperation(value = "", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map<String,Object> updateQ( + @ApiParam(value="Major Version",required=true) @PathParam("version") String version, + @ApiParam(value="Minor Version",required=false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value="Patch Version",required=false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value="AID",required=true) @HeaderParam("aid") String aid, + @ApiParam(value="Application namespace",required=true) @HeaderParam("ns") String ns, @ApiParam(value="userId",required=true) @HeaderParam("userId") String userId, + @ApiParam(value="Password",required=true) @HeaderParam("password") String password, JsonUpdate updateObj, + @ApiParam(value="Key Space",required=true) @PathParam("keyspace") String keyspace, + @ApiParam(value="Table Name",required=true) @PathParam("tablename") String tablename, + @Context UriInfo info, + @Context HttpServletResponse response) throws Exception{ + return new RestMusicDataAPI().updateTable(version,minorVersion,patchVersion,aid, ns, userId, password, updateObj, keyspace, tablename, info,response); + } + + /** + * + * @param delObj + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @DELETE + @Path("/keyspaces/{keyspace}/{qname}/rows") + @ApiOperation(value = "", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map<String,Object> deleteFromQ( + @ApiParam(value="Major Version",required=true) @PathParam("version") String version, + @ApiParam(value="Minor Version",required=false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value="Patch Version",required=false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value="AID",required=true) @HeaderParam("aid") String aid, + @ApiParam(value="Application namespace",required=true) @HeaderParam("ns") String ns, + @ApiParam(value="userId",required=true) @HeaderParam("userId") String userId, + @ApiParam(value="Password",required=true) @HeaderParam("password") String password, JsonDelete delObj, + @ApiParam(value="Key Space",required=true) @PathParam("keyspace") String keyspace, + @ApiParam(value="Table Name",required=true) @PathParam("tablename") String tablename, + @Context UriInfo info, + @Context HttpServletResponse response) throws Exception{ + return new RestMusicDataAPI().deleteFromTable(version,minorVersion,patchVersion,aid, ns, userId, password, delObj, keyspace, tablename, info,response); + } + + /** + * + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @GET + @Path("/keyspaces/{keyspace}/{qname}/peek") + @ApiOperation(value = "", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map<String, HashMap<String, Object>> peek( + @ApiParam(value="Major Version",required=true) @PathParam("version") String version, + @ApiParam(value="Minor Version",required=false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value="Patch Version",required=false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value="AID",required=true) @HeaderParam("aid") String aid, + @ApiParam(value="Application namespace",required=true) @HeaderParam("ns") String ns, + @ApiParam(value="userId",required=true) @HeaderParam("userId") String userId, + @ApiParam(value="Password",required=true) @HeaderParam("password") String password, + @ApiParam(value="Key Space",required=true) @PathParam("keyspace") String keyspace, + @ApiParam(value="Table Name",required=true) @PathParam("tablename") String tablename, + @Context UriInfo info) throws Exception{ + int limit =1; //peek must return just the top row + PreparedQueryObject query = new RestMusicDataAPI().selectSpecificQuery(version,minorVersion,patchVersion,aid, ns, userId, password,keyspace,tablename,info,limit); + ResultSet results = MusicCore.get(query); + return MusicCore.marshallResults(results); + + } + + /** + * + * + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @GET + @Path("/keyspaces/{keyspace}/{qname}/filter") + @ApiOperation(value = "", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map<String, HashMap<String, Object>> filter( + @ApiParam(value="Major Version",required=true) @PathParam("version") String version, + @ApiParam(value="Minor Version",required=false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value="Patch Version",required=false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value="AID",required=true) @HeaderParam("aid") String aid, + @ApiParam(value="Application namespace",required=true) @HeaderParam("ns") String ns, + @ApiParam(value="userId",required=true) @HeaderParam("userId") String userId, + @ApiParam(value="Password",required=true) @HeaderParam("password") String password, + @ApiParam(value="Key Space",required=true) @PathParam("keyspace") String keyspace, + @ApiParam(value="Table Name",required=true) @PathParam("tablename") String tablename, + @Context UriInfo info) throws Exception{ + int limit =-1; + PreparedQueryObject query = new RestMusicDataAPI().selectSpecificQuery(version,minorVersion,patchVersion,aid, ns, userId, password,keyspace,tablename,info,limit); + ResultSet results = MusicCore.get(query); + return MusicCore.marshallResults(results); + } + + /** + * + * @param tabObj + * @param keyspace + * @param tablename + * @throws Exception + */ + @DELETE + @ApiOperation(value = "", response = Void.class) + @Path("/keyspaces/{keyspace}/{qname}") + public Map<String,Object> dropQ( + @ApiParam(value="Major Version",required=true) @PathParam("version") String version, + @ApiParam(value="Minor Version",required=false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value="Patch Version",required=false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value="AID",required=true) @HeaderParam("aid") String aid, + @ApiParam(value="Application namespace",required=true) @HeaderParam("ns") String ns, + @ApiParam(value="userId",required=true) @HeaderParam("userId") String userId, + @ApiParam(value="Password",required=true) @HeaderParam("password") String password, JsonTable tabObj, + @ApiParam(value="Key Space",required=true) @PathParam("keyspace") String keyspace, + @ApiParam(value="Table Name",required=true) @PathParam("tablename") String tablename, + @Context HttpServletResponse response) throws Exception{ + return new RestMusicDataAPI().dropTable(version,minorVersion,patchVersion,aid, ns, userId, password, tabObj, keyspace, tablename,response); + } +} diff --git a/src/main/java/org/onap/music/rest/RestMusicTestAPI.java b/src/main/java/org/onap/music/rest/RestMusicTestAPI.java new file mode 100644 index 00000000..6b6bc101 --- /dev/null +++ b/src/main/java/org/onap/music/rest/RestMusicTestAPI.java @@ -0,0 +1,67 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.rest; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; + +import org.onap.music.main.MusicUtil; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; + + +@Path("/v{version: [0-9]+}/test") +@Api(value="Test Api") +public class RestMusicTestAPI { + private static EELFLogger logger = EELFManager.getInstance().getLogger(RestMusicTestAPI.class); + + /** + * Returns a test JSON. This will confirm that REST is working. + * @return + */ + @GET + @ApiOperation(value = "Get Test", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map<String, HashMap<String, String>> simpleTests( + @Context HttpServletResponse response) { + response.addHeader("X-latestVersion",MusicUtil.getVersion()); + Map<String, HashMap<String, String>> testMap = new HashMap<>(); + for(int i=0; i < 3; i++){ + HashMap<String, String> innerMap = new HashMap<>(); + innerMap.put(i+"", i+1+""); + innerMap.put(i+1+"", i+2+""); + testMap.put(i+"", innerMap); + } + return testMap; + } +} diff --git a/src/main/java/org/onap/music/rest/RestMusicVersionAPI.java b/src/main/java/org/onap/music/rest/RestMusicVersionAPI.java new file mode 100644 index 00000000..924b0289 --- /dev/null +++ b/src/main/java/org/onap/music/rest/RestMusicVersionAPI.java @@ -0,0 +1,61 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.rest; + +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; + +import org.onap.music.response.jsonobjects.JsonResponse; +import org.onap.music.main.MusicUtil; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; + + +@Path("/v{version: [0-9]+}/version") +@Api(value="Version Api") +public class RestMusicVersionAPI { + + private static EELFLogger logger = EELFManager.getInstance().getLogger(RestMusicVersionAPI.class); + /** + * Get the version of MUSIC + * @return + */ + @GET + @ApiOperation(value = "Get Version", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map<String,Object> version(@Context HttpServletResponse response) { + logger.info("Replying to request for MUSIC version with MUSIC:" + MusicUtil.getVersion()); + JsonResponse jResponse = new JsonResponse(true,"","MUSIC:" + MusicUtil.getVersion()); + response.addHeader("X-latestVersion",MusicUtil.getVersion()); + return jResponse.toMap(); + } +}
\ No newline at end of file diff --git a/src/main/resources/LICENSE.txt b/src/main/resources/LICENSE.txt new file mode 100644 index 00000000..cc6cdea5 --- /dev/null +++ b/src/main/resources/LICENSE.txt @@ -0,0 +1,24 @@ + +The following license applies to all files in this and sub-directories. Licenses +are included in individual source files where appropriate, and if it differs +from this text, it supersedes this. Any file that does not have license text +defaults to being covered by this text; not all files support the addition of +licenses. +# +# ------------------------------------------------------------------------- +# Copyright (c) 2017 AT&T Intellectual Property +# +# 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. +# +# ------------------------------------------------------------------------- +#
\ No newline at end of file diff --git a/src/main/resources/Resources.properties b/src/main/resources/Resources.properties new file mode 100644 index 00000000..7ee7baae --- /dev/null +++ b/src/main/resources/Resources.properties @@ -0,0 +1,49 @@ +#============LICENSE_START========================================== +#org.onap.music +#=================================================================== +# Copyright (c) 2017 AT&T Intellectual Property +#=================================================================== +# 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============================================= +#==================================================================== +#Resource key=Error Code|Message text|Resolution text |Description text +LOADING_DEFAULT_LOG_CONFIGURATION=\ + EELF0001I|\ + Loading default logging configuration from system resource file "{0}"|\ + No external logging configurations were defined or found, So verify the default logging configuration from system resource file (../logback.xml). |\ + Loading default logging configuration from system resource file +LOADING_LOG_CONFIGURATION=EELF0002I|\ + Loading logging configuration from file "{0}"|\ + Verify the correct logging configuration file is loaded. |\ + Loading logging configuration for specific file +LOGGING_ALREADY_INITIALIZED=\ + EELF0003W|\ + Logging has already been initialized, check the container logging definitions to ensure they represent your desired logging configuration.|\ + Verify the container logging definitions to ensure they represent your desired logging configuration. |\ + Logging has already been initialized, check the container logging definitions to ensure they represent your desired logging configuration. +NO_LOG_CONFIGURATION=\ + EELF0004E|\ + No log configuration could be found or defaulted!|\ + No external and default logging configuration file. |\ + No log configuration could be found or defaulted! +SEARCHING_LOG_CONFIGURATION=\ + EELF0005I|\ + Searching path "{0}" for log configuration file "{1}"|\ + Verify the correct Path({user.home};etc;../etc) and filename (eelf.logging.file).|\ + Searching path for specific log configuration file. +UNSUPPORTED_LOGGING_FRAMEWORK=\ + EELF0006E|\ + An unsupported logging framework is bound to SLF4J. |\ + Verify your logging frameworks.|\ + An unsupported logging framework is bound to SLF4J.
\ No newline at end of file diff --git a/src/main/resources/cache.ccf b/src/main/resources/cache.ccf new file mode 100644 index 00000000..acc6831c --- /dev/null +++ b/src/main/resources/cache.ccf @@ -0,0 +1,56 @@ +# DEFAULT CACHE REGION +jcs.default= +jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes +jcs.default.cacheattributes.MaxObjects=1000 +jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache +jcs.default.cacheattributes.UseMemoryShrinker=false +jcs.default.cacheattributes.MaxMemoryIdleTime=3600 +jcs.default.cacheattributes.ShrinkerInterval=60 +jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes +jcs.default.elementattributes.IsEternal=false +jcs.default.elementattributes.MaxLife=21600 +jcs.default.elementattributes.IdleTime=1800 +jcs.default.elementattributes.IsSpool=true +jcs.default.elementattributes.IsRemote=true +jcs.default.elementattributes.IsLateral=true + +# PRE-DEFINED CACHE REGIONS +jcs.region.musicCache= +jcs.region.musicCache.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes +jcs.region.musicCache.cacheattributes.MaxObjects=1000 +jcs.region.musicCache.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache +jcs.region.musicCache.cacheattributes.UseMemoryShrinker=false +jcs.region.musicCache.cacheattributes.MaxMemoryIdleTime=3600 +jcs.region.musicCache.cacheattributes.ShrinkerInterval=60 +jcs.region.musicCache.cacheattributes.MaxSpoolPerRun=500 +jcs.region.musicCache.elementattributes=org.apache.commons.jcs.engine.ElementAttributes +jcs.region.musicCache.elementattributes.IsEternal=false + + +# PRE-DEFINED CACHE REGIONS +jcs.region.aafCache= +jcs.region.aafCache.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes +jcs.region.aafCache.cacheattributes.MaxObjects=1000 +jcs.region.aafCache.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache +jcs.region.aafCache.cacheattributes.UseMemoryShrinker=false +jcs.region.aafCache.cacheattributes.MaxMemoryIdleTime=3600 +jcs.region.aafCache.cacheattributes.ShrinkerInterval=60 +jcs.region.aafCache.cacheattributes.MaxSpoolPerRun=500 +jcs.region.aafCache.elementattributes=org.apache.commons.jcs.engine.ElementAttributes +jcs.region.aafCache.elementattributes.IsEternal=false + +# PRE-DEFINED CACHE REGIONS +jcs.region.appNameCache= +jcs.region.appNameCache.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes +jcs.region.appNameCache.cacheattributes.MaxObjects=1000 +jcs.region.appNameCache.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache +jcs.region.appNameCache.cacheattributes.UseMemoryShrinker=false +jcs.region.appNameCache.cacheattributes.MaxMemoryIdleTime=3600 +jcs.region.appNameCache.cacheattributes.ShrinkerInterval=60 +jcs.region.appNameCache.cacheattributes.MaxSpoolPerRun=500 +jcs.region.appNameCache.elementattributes=org.apache.commons.jcs.engine.ElementAttributes +jcs.region.appNameCache.elementattributes.IsEternal=false + + + + diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 00000000..5378ebaf --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,269 @@ +<!-- + ============LICENSE_START========================================== + org.onap.music + =================================================================== + Copyright (c) 2017 AT&T Intellectual Property + =================================================================== + 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============================================= + ==================================================================== +--> +<configuration scan="true" scanPeriod="3 seconds"> + <!--<jmxConfigurator /> --> + <!-- directory path for all other type logs --> + <property name="logDir" value="/opt/app/music/logs" /> + + <!-- directory path for debugging type logs --> + <property name="debugDir" value="debug-logs" /> + + <!-- specify the component name --> + <!-- <property name="componentName" value="EELF"></property> --> + <property name="componentName" value="MUSIC"></property> + + <!-- log file names --> + <property name="generalLogName" value="music" /> + <property name="securityLogName" value="security" /> + <property name="errorLogName" value="error" /> + <property name="metricsLogName" value="metrics" /> + <property name="auditLogName" value="audit" /> + <property name="debugLogName" value="debug" /> + <property name="defaultPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n " /> + <property name="auditLoggerPattern" value="%X{BeginTimestamp}|%X{EndTimestamp}|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{VirtualServerName}|%X{ServiceName}|%X{PartnerName}|%X{StatusCode}|%X{ResponseCode}|%X{ResponseDescription}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}|%X{ServerIPAddress}|%X{ElapsedTime}|%X{ServerFQDN}|%X{RemoteHost}|%X{ClassName}|%X{Unused}|%X{ProcessKey}|%X{CustomField1}|%X{CustomField2}|%X{CustomField3}|%X{CustomField4}| %msg%n" /> + <property name="metricsLoggerPattern" value="%X{BeginTimestamp}|%X{EndTimestamp}|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{VirtualServerName}|%X{ServiceName}|%X{PartnerName}|%X{TargetEntity}|%X{TargetServiceName}|%X{StatusCode}|%X{ResponseCode}|%X{ResponseDescription}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}|%X{ServerIPAddress}|%X{ElapsedTime}|%X{ServerFQDN}|%X{RemoteHost}|%X{ClassName}|%X{Unused}|%X{ProcessKey}|%X{TargetVirtualEntity}|%X{CustomField1}|%X{CustomField2}|%X{CustomField3}|%X{CustomField4}| %msg%n" /> + <property name="errorLoggerPattern" value="%date{ISO8601,UTC}|%X{RequestId}|%thread|%X{ServiceName}|%X{PartnerName}|%X{TargetEntity}|%X{TargetServiceName}|%.-5level|%X{ErrorCode}|%X{ErrorDescription}| %msg%n" /> + <property name="debugLoggerPattern" value="%date{ISO8601,UTC}|%X{RequestId}| %msg%n" ></property> + <property name="logDirectory" value="${logDir}/${componentName}" /> + <property name="debugLogDirectory" value="${debugDir}/${componentName}" /> + <!-- Example evaluator filter applied against console appender --> + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <!-- <encoder> + <pattern>${defaultPattern}</pattern> + </encoder> --> + <layout class=""> + <pattern> + %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n + </pattern> + </layout> + </appender> + + <!-- ============================================================================ --> + <!-- EELF Appenders --> + <!-- ============================================================================ --> + <appender name="EELF" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${logDirectory}/${generalLogName}.log</file> + <rollingPolicy + class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> + <fileNamePattern>${logDirectory}/${generalLogName}.%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>${defaultPattern}</pattern> + </encoder> + </appender> + + <appender name="asyncEELF" class="ch.qos.logback.classic.AsyncAppender"> + <queueSize>256</queueSize> + <includeCallerData>true</includeCallerData> + <appender-ref ref="EELF" /> + </appender> + + <!-- EELF Security Appender. This appender is used to record security events + to the security log file. Security events are separate from other loggers + in EELF so that security log records can be captured and managed in a secure + way separate from the other logs. This appender is set to never discard any + events. --> + <appender name="EELFSecurity" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${logDirectory}/${securityLogName}.log</file> + <rollingPolicy + class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> + <fileNamePattern>${logDirectory}/${securityLogName}.%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{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n </pattern> + </encoder> + </appender> + + <appender name="asyncEELFSecurity" class="ch.qos.logback.classic.AsyncAppender"> + <queueSize>256</queueSize> + <discardingThreshold>0</discardingThreshold> + <appender-ref ref="EELFSecurity" /> + </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.FixedWindowRollingPolicy"> + <fileNamePattern>${logDirectory}/${auditLogName}.%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>${auditLoggerPattern}</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.FixedWindowRollingPolicy"> + <fileNamePattern>${logDirectory}/${metricsLogName}.%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>${metricsLoggerPattern}</pattern> + </encoder> + </appender> + + + <appender name="asyncEELFMetrics" class="ch.qos.logback.classic.AsyncAppender"> + <queueSize>256</queueSize> + <appender-ref ref="EELFMetrics"/> + </appender> + + <appender name="EELFError" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${logDirectory}/${errorLogName}.log</file> + <rollingPolicy + class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> + <fileNamePattern>${logDirectory}/${errorLogName}.%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>${defaultPattern}</pattern> + </encoder> + </appender> + + <appender name="asyncEELFError" class="ch.qos.logback.classic.AsyncAppender"> + <queueSize>256</queueSize> + <appender-ref ref="EELFError"/> + </appender> + + <appender name="EELFDebug" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${debugLogDirectory}/${debugLogName}.log</file> + <rollingPolicy + class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> + <fileNamePattern>${debugLogDirectory}/${debugLogName}.%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>${defaultPattern}</pattern> + </encoder> + </appender> + + <appender name="asyncEELFDebug" class="ch.qos.logback.classic.AsyncAppender"> + <queueSize>256</queueSize> + <appender-ref ref="EELFDebug" /> + <includeCallerData>true</includeCallerData> + </appender> + + + <!-- ============================================================================ --> + <!-- EELF loggers --> + <!-- ============================================================================ --> + <logger name="com.att.eelf" level="info" additivity="false"> + <appender-ref ref="asyncEELF" /> + + </logger> + <logger name="com.att.eelf.security" level="info" additivity="false"> + <appender-ref ref="asyncEELFSecurity" /> + + </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> + + + <logger name="com.att.eelf.error" level="error" additivity="false"> + <appender-ref ref="asyncEELFError" /> + + </logger> + + <logger name="com.att.eelf.debug" level="debug" additivity="false"> + <appender-ref ref="asyncEELFDebug" /> + + </logger> + + + + + <root level="INFO"> + <appender-ref ref="asyncEELF" /> + <appender-ref ref="STDOUT" /> + </root> + +</configuration> diff --git a/src/main/resources/project.properties b/src/main/resources/project.properties new file mode 100644 index 00000000..199afa33 --- /dev/null +++ b/src/main/resources/project.properties @@ -0,0 +1,4 @@ +version=${project.version} +artifactId=${project.artifactId} +music.properties=/opt/app/music/etc/music.properties + diff --git a/src/test/java/LICENSE.txt b/src/test/java/LICENSE.txt new file mode 100644 index 00000000..cc6cdea5 --- /dev/null +++ b/src/test/java/LICENSE.txt @@ -0,0 +1,24 @@ + +The following license applies to all files in this and sub-directories. Licenses +are included in individual source files where appropriate, and if it differs +from this text, it supersedes this. Any file that does not have license text +defaults to being covered by this text; not all files support the addition of +licenses. +# +# ------------------------------------------------------------------------- +# Copyright (c) 2017 AT&T Intellectual Property +# +# 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. +# +# ------------------------------------------------------------------------- +#
\ No newline at end of file diff --git a/src/test/java/org/onap/music/benchmarks/MicroBenchMarkRestClient.java b/src/test/java/org/onap/music/benchmarks/MicroBenchMarkRestClient.java new file mode 100644 index 00000000..2dcecfc2 --- /dev/null +++ b/src/test/java/org/onap/music/benchmarks/MicroBenchMarkRestClient.java @@ -0,0 +1,73 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.benchmarks; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import org.onap.music.main.MusicUtil; + + +@Path("/tests") +public class MicroBenchMarkRestClient { + static double version = 9.7; + + @GET + @Path("/run") + @Produces(MediaType.TEXT_PLAIN) + public String runTests() { + String testType = MusicUtil.getTestType(); + String candidateName = "shankar" + System.currentTimeMillis(); + MicroBenchMarks msHandle = new MicroBenchMarks(); + switch (testType) { + case "musicPut": + return msHandle.musicPutAndUpdate(candidateName); + + case "musicCriticalPut": + return msHandle.musicCriticalPutAndUpdate(candidateName); + + case "musicGet": + return msHandle.musicGet(); + + case "cassaPut": + return msHandle.cassaPutAndUpdate(candidateName); + + case "cassaQuorumPut": + return msHandle.cassaQuorumPutAndUpdate(candidateName); + + case "cassaGet": + return msHandle.cassaGet(); + + case "zkPut": + return msHandle.zkPutAndUpdate(candidateName); + + case "zkGet": + return msHandle.zkGet(); + + } + return "something wrong!"; + + } + +} + diff --git a/src/test/java/org/onap/music/benchmarks/MicroBenchMarks.java b/src/test/java/org/onap/music/benchmarks/MicroBenchMarks.java new file mode 100644 index 00000000..a9b40aec --- /dev/null +++ b/src/test/java/org/onap/music/benchmarks/MicroBenchMarks.java @@ -0,0 +1,440 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.benchmarks; + +import java.util.HashMap; +import java.util.Map; +import javax.ws.rs.core.MediaType; +import org.onap.music.datastore.jsonobjects.JsonInsert; +import org.onap.music.datastore.jsonobjects.JsonKeySpace; +import org.onap.music.datastore.jsonobjects.JsonTable; +import org.onap.music.main.MusicUtil; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.api.client.config.ClientConfig; +import com.sun.jersey.api.client.config.DefaultClientConfig; +import com.sun.jersey.api.json.JSONConfiguration; + +public class MicroBenchMarks { + final String keyspaceName = "shankarks"; + final String musicurl = "http://" + MusicUtil.musicRestIp + ":8080/MUSIC/rest/formal"; + final String userForGets = "shankarUserForGets"; + + public MicroBenchMarks() { + bootStrap(); + } + + private void createVotingKeyspace() { + System.out.println(keyspaceName); + Map<String, Object> replicationInfo = new HashMap<String, Object>(); + replicationInfo.put("class", "SimpleStrategy"); + replicationInfo.put("replication_factor", 3); + String durabilityOfWrites = "false"; + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "eventual"); + JsonKeySpace jsonKp = new JsonKeySpace(); + jsonKp.setConsistencyInfo(consistencyInfo); + jsonKp.setDurabilityOfWrites(durabilityOfWrites); + jsonKp.setReplicationInfo(replicationInfo); + + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + + WebResource webResource = client.resource(musicurl + "/keyspaces/" + keyspaceName); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .post(ClientResponse.class, jsonKp); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + } + + private void createVotingTable() { + Map<String, String> fields = new HashMap<String, String>(); + fields.put("name", "text"); + fields.put("count", "varint"); + fields.put("PRIMARY KEY", "(name)"); + + + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "eventual"); + + JsonTable jtab = new JsonTable(); + jtab.setFields(fields); + jtab.setConsistencyInfo(consistencyInfo); + + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + + WebResource webResource = client + .resource(musicurl + "/keyspaces/" + keyspaceName + "/tables/votecount"); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .post(ClientResponse.class, jtab); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + } + + private void createEntryForCandidate(String candidateName) { + Map<String, Object> values = new HashMap<String, Object>(); + values.put("name", candidateName); + values.put("count", 0); + + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "eventual"); + + JsonInsert jIns = new JsonInsert(); + jIns.setValues(values); + jIns.setConsistencyInfo(consistencyInfo); + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + + String url = musicurl + "/keyspaces/" + keyspaceName + "/tables/votecount/rows"; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .post(ClientResponse.class, jIns); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + + private String createLock(String lockName) { + Client client = Client.create(); + String msg = musicurl + "/locks/create/" + lockName; + WebResource webResource = client.resource(msg); + System.out.println(msg); + + WebResource.Builder wb = webResource.accept(MediaType.TEXT_PLAIN); + + ClientResponse response = wb.post(ClientResponse.class); + + if (response.getStatus() != 200) { + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + + String output = response.getEntity(String.class); + + return output; + } + + private boolean acquireLock(String lockId) { + Client client = Client.create(); + String msg = musicurl + "/locks/acquire/" + lockId; + System.out.println(msg); + WebResource webResource = client.resource(msg); + + + WebResource.Builder wb = webResource.accept(MediaType.TEXT_PLAIN); + + ClientResponse response = wb.get(ClientResponse.class); + + if (response.getStatus() != 200) { + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + + String output = response.getEntity(String.class); + Boolean status = Boolean.parseBoolean(output); + System.out.println("Server response .... \n"); + System.out.println(output); + return status; + } + + private void unlock(String lockId) { + Client client = Client.create(); + WebResource webResource = client.resource(musicurl + "/locks/release/" + lockId); + + ClientResponse response = webResource.delete(ClientResponse.class); + + + if (response.getStatus() != 204) { + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + } + + public String musicCriticalPutAndUpdate(String candidateName) { + /* + * create lock for the candidate. The music API dictates that the lock name must be of the + * form keyspacename.tableName.primaryKeyName + */ + createEntryForCandidate(candidateName); + System.out.println("trying to acquire lock!"); + + String lockName = keyspaceName + ".votecount." + candidateName; + String lockId = createLock(lockName); + while (acquireLock(lockId) != true); + + System.out.println("acquired lock!"); + // update candidate entry if you have the lock + Map<String, Object> values = new HashMap<String, Object>(); + values.put("count", 5); + + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "atomic"); + consistencyInfo.put("lockId", lockId); + + JsonInsert jIns = new JsonInsert(); + jIns.setValues(values); + jIns.setConsistencyInfo(consistencyInfo); + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = musicurl + "/keyspaces/" + keyspaceName + "/tables/votecount/rows?name=" + + candidateName; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .put(ClientResponse.class, jIns); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + // release lock now that the operation is done + unlock(lockId); + return "musicCriticalPutAndUpdate:" + url; + + } + + public String musicPutAndUpdate(String candidateName) { + createEntryForCandidate(candidateName); + + Map<String, Object> values = new HashMap<String, Object>(); + values.put("count", 5); + + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "eventual"); + + JsonInsert jIns = new JsonInsert(); + jIns.setValues(values); + jIns.setConsistencyInfo(consistencyInfo); + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = musicurl + "/keyspaces/" + keyspaceName + "/tables/votecount/rows?name=" + + candidateName; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .put(ClientResponse.class, jIns); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + return "musicPutAndUpdate:" + url; + } + + public String musicGet() { + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + + String url = musicurl + "/keyspaces/" + keyspaceName + "/tables/votecount/rows?name=" + + userForGets; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").get(ClientResponse.class); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + Map<String, Object> output = response.getEntity(Map.class); + return "musicGet:" + url; + } + + public String cassaQuorumPutAndUpdate(String candidateName) { + // http://135.197.226.98:8080/MUSIC/rest/formal/purecassa/keyspaces/shankarks/tables/employees/rows?emp_name=shankaruser1 + Map<String, Object> values = new HashMap<String, Object>(); + values.put("count", 5); + + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "atomic"); + + JsonInsert jIns = new JsonInsert(); + jIns.setValues(values); + jIns.setConsistencyInfo(consistencyInfo); + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = musicurl + "/purecassa/keyspaces/" + keyspaceName + + "/tables/votecount/rows?name=" + candidateName; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .put(ClientResponse.class, jIns); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + return "cassaQuorumPutAndUpdate:" + url; + + } + + public String cassaPutAndUpdate(String candidateName) { + // http://135.197.226.98:8080/MUSIC/rest/formal/purecassa/keyspaces/shankarks/tables/employees/rows?emp_name=shankaruser1 + long start = System.currentTimeMillis(); + createEntryForCandidate(candidateName); + + Map<String, Object> values = new HashMap<String, Object>(); + values.put("count", 5); + + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "eventual"); + + JsonInsert jIns = new JsonInsert(); + jIns.setValues(values); + jIns.setConsistencyInfo(consistencyInfo); + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = musicurl + "/purecassa/keyspaces/" + keyspaceName + + "/tables/votecount/rows?name=" + candidateName; + WebResource webResource = client.resource(url); + long end = System.currentTimeMillis(); + String time = (end - start) + ""; + + start = System.currentTimeMillis(); + ClientResponse response = webResource.accept("application/json").type("application/json") + .put(ClientResponse.class, jIns); + end = System.currentTimeMillis(); + String time2 = (end - start) + ""; + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + return "cassaPutAndUpdate:" + url; + } + + public String cassaGet() { + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + + String url = musicurl + "/keyspaces/" + keyspaceName + "/tables/votecount/rows?name=" + + userForGets; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").get(ClientResponse.class); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + Map<String, Object> output = response.getEntity(Map.class); + return "cassaGet:" + url; + } + + private void zkCreate(String candidateName) { + // http://135.197.226.98:8080/MUSIC/rest/formal/purezk/shankarzknode + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + + WebResource webResource = client.resource(musicurl + "/purezk/" + candidateName); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .post(ClientResponse.class); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + + public String zkPutAndUpdate(String candidateName) { + // http://135.197.226.99:8080/MUSIC/rest/formal/purezk/shankarzknode + + // CREATE IT FIRST + zkCreate(candidateName); + + Map<String, Object> values = new HashMap<String, Object>(); + values.put("count", 5); + + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "atomic"); + + JsonInsert jIns = new JsonInsert(); + jIns.setValues(values); + jIns.setConsistencyInfo(consistencyInfo); + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = musicurl + "/purezk/" + candidateName; + System.out.println("in zk put:" + url); + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .put(ClientResponse.class, jIns); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + return "zkPutAndUpdate:" + url; + } + + public String zkGet() { + Client client = Client.create(); + String url = musicurl + "/purezk/" + userForGets; + System.out.println("in zk get:" + url); + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("text/plain").get(ClientResponse.class); + + if (response.getStatus() != 200) { + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + + String output = response.getEntity(String.class); + return "zkGet:" + url; + } + + + public void bootStrap() { + // createVotingKeyspace(); + // createVotingTable(); + // createEntryForCandidate(userForGets); + // zkPutAndUpdate(userForGets); + } + + +} diff --git a/src/test/java/org/onap/music/e2eTests/MusicConnector.java b/src/test/java/org/onap/music/e2eTests/MusicConnector.java new file mode 100644 index 00000000..bb3d1f1b --- /dev/null +++ b/src/test/java/org/onap/music/e2eTests/MusicConnector.java @@ -0,0 +1,80 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.e2eTests; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Random; + +public class MusicConnector { + + // change this to point to relevant cluster + public String[] musicNodes; + + public MusicConnector(String[] musicNodes) { + this.musicNodes = musicNodes; + } + + private String getMusicNodeIp() { + Random r = new Random(); + int index = r.nextInt(musicNodes.length); + return musicNodes[index]; + } + + /* + * public static String toggle(String serverAddress){ if(serverAddress.equals(agaveMusicNode)){ + * System.out.println("Agave is down...connect to Big Site"); serverAddress = bigSiteMusicNode; + * }else if(serverAddress.equals(bigSiteMusicNode)){ + * System.out.println("Big Site is down...connect to Agave"); serverAddress = agaveMusicNode; } + * return serverAddress; } + */ + + public String getMusicNodeURL() { + String musicurl = "http://" + getMusicNodeIp() + ":8080/MUSIC/rest"; + System.out.println(musicurl); + return musicurl; + } + + public boolean isHostUp(String serverAddress) { + Boolean isUp = false; + try { + InetAddress inet = InetAddress.getByName(serverAddress); + isUp = inet.isReachable(1000); + } catch (UnknownHostException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return isUp; + } + + /* + * private static String getMusicNodeIp(){ + * + * //return "54.224.168.13"; return bigSiteMusicNode; String serverAddress; serverAddress = + * agaveMusicNode; while(isHostUp(serverAddress) != true) serverAddress = toggle(serverAddress); + * return serverAddress; } + */ +} diff --git a/src/test/java/org/onap/music/e2eTests/TestMusicE2E.java b/src/test/java/org/onap/music/e2eTests/TestMusicE2E.java new file mode 100644 index 00000000..25021e82 --- /dev/null +++ b/src/test/java/org/onap/music/e2eTests/TestMusicE2E.java @@ -0,0 +1,478 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.e2eTests; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import javax.ws.rs.core.MediaType; +import org.onap.music.datastore.jsonobjects.JsonDelete; +import org.onap.music.datastore.jsonobjects.JsonInsert; +import org.onap.music.datastore.jsonobjects.JsonKeySpace; +import org.onap.music.datastore.jsonobjects.JsonTable; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.api.client.config.ClientConfig; +import com.sun.jersey.api.client.config.DefaultClientConfig; +import com.sun.jersey.api.json.JSONConfiguration; + +public class TestMusicE2E { + String keyspaceName; + ArrayList<String> lockNames; + MusicConnector musicHandle; + + public TestMusicE2E(String[] musicIps) { + lockNames = new ArrayList<String>(); + musicHandle = new MusicConnector(musicIps); + bootStrap(); + } + + public void createVotingKeyspace() { + keyspaceName = "VotingAppForMusic" + System.currentTimeMillis(); + Map<String, Object> replicationInfo = new HashMap<String, Object>(); + replicationInfo.put("class", "SimpleStrategy"); + replicationInfo.put("replication_factor", 1); + String durabilityOfWrites = "false"; + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "eventual"); + JsonKeySpace jsonKp = new JsonKeySpace(); + jsonKp.setConsistencyInfo(consistencyInfo); + jsonKp.setDurabilityOfWrites(durabilityOfWrites); + jsonKp.setReplicationInfo(replicationInfo); + + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + + WebResource webResource = client + .resource(musicHandle.getMusicNodeURL() + "/keyspaces/" + keyspaceName); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .post(ClientResponse.class, jsonKp); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + } + + public void createVotingTable() { + Map<String, String> fields = new HashMap<String, String>(); + fields.put("name", "text"); + fields.put("count", "varint"); + fields.put("PRIMARY KEY", "(name)"); + + + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "eventual"); + + JsonTable jtab = new JsonTable(); + jtab.setFields(fields); + jtab.setConsistencyInfo(consistencyInfo); + + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = musicHandle.getMusicNodeURL() + "/keyspaces/" + keyspaceName + + "/tables/votecount"; + System.out.println("create url:" + url); + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .post(ClientResponse.class, jtab); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + } + + private void checkMusicVersion() { + Client client = Client.create(); + System.out.println(musicHandle.getMusicNodeURL() + "/version"); + + + // System.setProperty("sun.net.http.allowRestrictedHeaders", "true"); + WebResource webResource = client.resource(musicHandle.getMusicNodeURL() + "/version"); + + + ClientResponse response = webResource.accept("text/plain").header("Connection", "close") + .get(ClientResponse.class); + + if (response.getStatus() != 200) { + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + + String output = response.getEntity(String.class); + + // System.out.println("Output from Server .... \n"); + System.out.println(output); + + } + + private void createEntryForCandidate(String candidateName) { + Map<String, Object> values = new HashMap<String, Object>(); + values.put("name", candidateName); + values.put("count", 0); + + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "eventual"); + + JsonInsert jIns = new JsonInsert(); + jIns.setValues(values); + jIns.setConsistencyInfo(consistencyInfo); + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + + String url = musicHandle.getMusicNodeURL() + "/keyspaces/" + keyspaceName + + "/tables/votecount/rows"; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .post(ClientResponse.class, jIns); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus() + "url:" + + url + " candidate name:" + candidateName); + + + } + + private String createLock(String lockName) { + Client client = Client.create(); + String msg = musicHandle.getMusicNodeURL() + "/locks/create/" + lockName; + WebResource webResource = client.resource(msg); + System.out.println(msg); + WebResource.Builder wb = webResource.accept(MediaType.TEXT_PLAIN); + + ClientResponse response = wb.post(ClientResponse.class); + + if (response.getStatus() != 200) { + throw new RuntimeException( + "Failed : HTTP error code : " + response.getStatus() + "url:" + msg); + } + + String output = response.getEntity(String.class); + + // System.out.println("Server response .... \n"); + // System.out.println(output); + return output; + } + + private boolean acquireLock(String lockId) { + Client client = Client.create(); + String msg = musicHandle.getMusicNodeURL() + "/locks/acquire/" + lockId; + System.out.println(msg); + WebResource webResource = client.resource(msg); + + + WebResource.Builder wb = webResource.accept(MediaType.TEXT_PLAIN); + + ClientResponse response = wb.get(ClientResponse.class); + + if (response.getStatus() != 200) { + throw new RuntimeException( + "Failed : HTTP error code : " + response.getStatus() + "url:" + msg); + } + + String output = response.getEntity(String.class); + Boolean status = Boolean.parseBoolean(output); + System.out.println("Server response .... \n"); + System.out.println(output); + return status; + } + + private void unlock(String lockId) { + Client client = Client.create(); + WebResource webResource = + client.resource(musicHandle.getMusicNodeURL() + "/locks/release/" + lockId); + + ClientResponse response = webResource.delete(ClientResponse.class); + + + if (response.getStatus() != 204) { + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + } + + private void updateVoteCountAtomically(String candidateName, int count) { + /* + * create lock for the candidate. The music API dictates that the lock name must be of the + * form keyspacename.tableName.primaryKeyName + */ + System.out.println("trying to acquire lock!"); + + String lockName = keyspaceName + ".votecount." + candidateName; + lockNames.add(lockName); + String lockId = createLock(lockName); + while (acquireLock(lockId) != true); + + System.out.println("acquired lock!"); + // update candidate entry if you have the lock + Map<String, Object> values = new HashMap<String, Object>(); + values.put("count", count); + + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "critical"); + consistencyInfo.put("lockId", lockId); + + JsonInsert jIns = new JsonInsert(); + jIns.setValues(values); + jIns.setConsistencyInfo(consistencyInfo); + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = musicHandle.getMusicNodeURL() + "/keyspaces/" + keyspaceName + + "/tables/votecount/rows?name=" + candidateName; + System.out.println(url); + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .put(ClientResponse.class, jIns); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException( + "Failed : HTTP error code : " + response.getStatus() + "url:" + url); + + // release lock now that the operation is done + unlock(lockId); + + } + + private void deleteCandidateEntryEventually(String candidateName) { + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "eventual"); + + JsonDelete jDel = new JsonDelete(); + jDel.setConsistencyInfo(consistencyInfo); + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = musicHandle.getMusicNodeURL() + "/keyspaces/" + keyspaceName + + "/tables/votecount/rows?name=" + candidateName; + System.out.println(url); + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .delete(ClientResponse.class, jDel); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException( + "Failed : HTTP error code : " + response.getStatus() + "url:" + url); + + } + + public Map<String, Object> readVoteCountForCandidate(String candidateName) { + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = musicHandle.getMusicNodeURL() + "/keyspaces/" + keyspaceName + + "/tables/votecount/rows?name=" + candidateName; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").get(ClientResponse.class); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + Map<String, Object> output = response.getEntity(Map.class); + return output; + } + + public Map<String, Object> readAllVotes() { + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = musicHandle.getMusicNodeURL() + "/keyspaces/" + keyspaceName + + "/tables/votecount/rows"; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").get(ClientResponse.class); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + Map<String, Object> output = response.getEntity(Map.class); + return output; + } + + + /* + * Unable to use this because of the error: Exception in thread "main" + * com.sun.jersey.api.client.ClientHandlerException: java.net.ProtocolException: HTTP method + * DELETE doesn't support output. Seems to be a error in the rest java combination according to + * the interwebs + */ + private void dropKeySpace() { + Map<String, String> consistencyInfo = new HashMap<String, String>(); + consistencyInfo.put("type", "eventual"); + + JsonKeySpace jsonKp = new JsonKeySpace(); + jsonKp.setConsistencyInfo(consistencyInfo); + + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + + WebResource webResource = client + .resource(musicHandle.getMusicNodeURL() + "/keyspaces/" + keyspaceName); + + ClientResponse response = + webResource.type("application/json").delete(ClientResponse.class, jsonKp); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + + private void deleteLock(String lockName) { + Client client = Client.create(); + WebResource webResource = client + .resource(musicHandle.getMusicNodeURL() + "/locks/delete/" + lockName); + + ClientResponse response = webResource.delete(ClientResponse.class); + + + if (response.getStatus() != 204) { + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + } + + private void resetMusic() { + Client client = Client.create(); + WebResource webResource = client.resource(musicHandle.getMusicNodeURL() + "/reset"); + + ClientResponse response = webResource.delete(ClientResponse.class); + + + if (response.getStatus() != 204) { + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + + } + + public void deleteAllLocks() { + for (String lockName : lockNames) { + deleteLock(lockName); + } + } + + + public void bootStrap() { + checkMusicVersion(); + createVotingKeyspace(); + + + createVotingTable(); + + + // the next few lines just create an entry in the voting table for all these candidates with + // vote count as 0 + createEntryForCandidate("Popeye"); + + createEntryForCandidate("Judy"); + + createEntryForCandidate("Flash"); + + createEntryForCandidate("Mickey"); + + } + + public void overAllTests() { + // update the count atomically + updateVoteCountAtomically("Popeye", 5); + + updateVoteCountAtomically("Judy", 7); + + updateVoteCountAtomically("Mickey", 8); + + updateVoteCountAtomically("Flash", 2); + + + // read votecount + System.out.println(readAllVotes()); + + System.out.println(readVoteCountForCandidate("Popeye")); + + System.out.println(readVoteCountForCandidate("Flash")); + + deleteCandidateEntryEventually("Mickey"); + + System.out.println(readAllVotes()); + + dropKeySpace(); + + deleteAllLocks(); + } + + public void flipTest() { + checkMusicVersion(); + } + + public static String executeBashScript(String pathToScript, String arg1, String arg2) { + try { + ProcessBuilder pb = new ProcessBuilder(pathToScript, arg1, arg2); + final Process process = pb.start(); + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + return br.readLine(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public static void main(String[] args) { + long start = System.currentTimeMillis(); + for (int i = 0; i < 2; ++i) { + TestMusicE2E vHandle = new TestMusicE2E(args); + vHandle.overAllTests(); + + System.out.println("====================================="); + System.out.println("Test no." + i + " completed:"); + } + long diff = System.currentTimeMillis() - start; + System.out.println(diff); + } + + +} diff --git a/src/test/java/org/onap/music/unittests/CassandraCQL.java b/src/test/java/org/onap/music/unittests/CassandraCQL.java new file mode 100644 index 00000000..0d2a606d --- /dev/null +++ b/src/test/java/org/onap/music/unittests/CassandraCQL.java @@ -0,0 +1,252 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.unittests; +/** + * @author srupane + * + */ + +import java.io.IOException; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import org.apache.cassandra.exceptions.ConfigurationException; +import org.apache.thrift.transport.TTransportException; +import org.cassandraunit.utils.EmbeddedCassandraServerHelper; +import org.onap.music.datastore.MusicDataStore; +import org.onap.music.datastore.PreparedQueryObject; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.exceptions.NoHostAvailableException; + +public class CassandraCQL { + + public static final String createKeySpace = + "CREATE KEYSPACE IF NOT EXISTS testCassa WITH replication = {'class':'SimpleStrategy','replication_factor':1} AND durable_writes = true;"; + + public static final String dropKeyspace = "DROP KEYSPACE IF EXISTS testCassa"; + + public static final String createTableEmployees = + "CREATE TABLE IF NOT EXISTS testCassa.employees " + + "(vector_ts text,emp_id uuid,emp_name text,emp_salary varint,address Map<text,text>,PRIMARY KEY (emp_name)) " + + "WITH comment='Financial Info of employees' " + + "AND compression={'sstable_compression':'DeflateCompressor','chunk_length_kb':64} " + + "AND compaction={'class':'SizeTieredCompactionStrategy','min_threshold':6};"; + + public static final String insertIntoTablePrepared1 = + "INSERT INTO testCassa.employees (vector_ts,emp_id,emp_name,emp_salary) VALUES (?,?,?,?); "; + + public static final String insertIntoTablePrepared2 = + "INSERT INTO testCassa.employees (vector_ts,emp_id,emp_name,emp_salary,address) VALUES (?,?,?,?,?);"; + + public static final String selectALL = "SELECT * FROM testCassa.employees;"; + + public static final String selectSpecific = + "SELECT * FROM testCassa.employees WHERE emp_name= ?;"; + + public static final String updatePreparedQuery = + "UPDATE testCassa.employees SET vector_ts=?,address= ? WHERE emp_name= ?;"; + + public static final String deleteFromTable = " "; + + public static final String deleteFromTablePrepared = " "; + + // Set Values for Prepared Query + + public static List<Object> setPreparedInsertValues1() { + + List<Object> preppreparedInsertValues1 = new ArrayList<>(); + String vectorTs = + String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis()); + UUID emp_id = UUID.fromString("abc66ccc-d857-4e90-b1e5-df98a3d40cd6"); + BigInteger emp_salary = BigInteger.valueOf(23443); + String emp_name = "Mr Test one"; + preppreparedInsertValues1.add(vectorTs); + preppreparedInsertValues1.add(emp_id); + preppreparedInsertValues1.add(emp_name); + preppreparedInsertValues1.add(emp_salary); + return preppreparedInsertValues1; + } + + public static List<Object> setPreparedInsertValues2() { + + List<Object> preparedInsertValues2 = new ArrayList<>(); + String vectorTs = + String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis()); + UUID emp_id = UUID.fromString("abc434cc-d657-4e90-b4e5-df4223d40cd6"); + BigInteger emp_salary = BigInteger.valueOf(45655); + String emp_name = "Mr Test two"; + Map<String, String> address = new HashMap<>(); + preparedInsertValues2.add(vectorTs); + preparedInsertValues2.add(emp_id); + preparedInsertValues2.add(emp_name); + preparedInsertValues2.add(emp_salary); + address.put("Street", "1 att way"); + address.put("City", "Bedmister"); + preparedInsertValues2.add(address); + return preparedInsertValues2; + } + + public static List<Object> setPreparedUpdateValues() { + + List<Object> preparedUpdateValues = new ArrayList<>(); + String vectorTs = + String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis()); + Map<String, String> address = new HashMap<>(); + preparedUpdateValues.add(vectorTs); + String emp_name = "Mr Test one"; + address.put("Street", "101 Att Way"); + address.put("City", "Bedmister"); + preparedUpdateValues.add(address); + preparedUpdateValues.add(emp_name); + return preparedUpdateValues; + } + + // Generate Different Prepared Query Objects + /** + * Query Object for Get + * + * @return + */ + public static PreparedQueryObject setPreparedGetQuery() { + + PreparedQueryObject queryObject = new PreparedQueryObject(); + String emp_name1 = "Mr Test one"; + queryObject.appendQueryString(selectSpecific); + queryObject.addValue(emp_name1); + return queryObject; + } + + /** + * Query Object 1 for Insert + * + * @return + */ + public static PreparedQueryObject setPreparedInsertQueryObject1() { + + PreparedQueryObject queryobject = new PreparedQueryObject(); + queryobject.appendQueryString(insertIntoTablePrepared1); + List<Object> values = setPreparedInsertValues1(); + if (!values.isEmpty() || values != null) { + for (Object o : values) { + queryobject.addValue(o); + } + } + return queryobject; + + } + + /** + * Query Object 2 for Insert + * + * @return + */ + public static PreparedQueryObject setPreparedInsertQueryObject2() { + + PreparedQueryObject queryobject = new PreparedQueryObject(); + queryobject.appendQueryString(insertIntoTablePrepared2); + List<Object> values = setPreparedInsertValues2(); + if (!values.isEmpty() || values != null) { + for (Object o : values) { + queryobject.addValue(o); + } + } + return queryobject; + + } + + /** + * Query Object for Update + * + * @return + */ + public static PreparedQueryObject setPreparedUpdateQueryObject() { + + PreparedQueryObject queryobject = new PreparedQueryObject(); + queryobject.appendQueryString(updatePreparedQuery); + List<Object> values = setPreparedUpdateValues(); + if (!values.isEmpty() || values != null) { + for (Object o : values) { + queryobject.addValue(o); + } + } + return queryobject; + + } + + private static ArrayList<String> getAllPossibleLocalIps() { + ArrayList<String> allPossibleIps = new ArrayList<String>(); + try { + Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); + while (en.hasMoreElements()) { + NetworkInterface ni = (NetworkInterface) en.nextElement(); + Enumeration<InetAddress> ee = ni.getInetAddresses(); + while (ee.hasMoreElements()) { + InetAddress ia = (InetAddress) ee.nextElement(); + allPossibleIps.add(ia.getHostAddress()); + } + } + } catch (SocketException e) { + System.out.println(e.getMessage()); + } + return allPossibleIps; + } + + public static MusicDataStore connectToEmbeddedCassandra() { + Iterator<String> it = getAllPossibleLocalIps().iterator(); + String address = "localhost"; + + Cluster cluster = null; + Session session = null; + while (it.hasNext()) { + try { + + try { + EmbeddedCassandraServerHelper.startEmbeddedCassandra(60000); + } catch (ConfigurationException | TTransportException | IOException e) { + + System.out.println(e.getMessage()); + } + + cluster = new Cluster.Builder().addContactPoint(address).withPort(9142).build(); + session = cluster.connect(); + + break; + } catch (NoHostAvailableException e) { + address = it.next(); + System.out.println(e.getMessage()); + + } + } + return new MusicDataStore(cluster, session); + + } + +} diff --git a/src/test/java/org/onap/music/unittests/MusicDataStoreTest.java b/src/test/java/org/onap/music/unittests/MusicDataStoreTest.java new file mode 100644 index 00000000..520a5fe4 --- /dev/null +++ b/src/test/java/org/onap/music/unittests/MusicDataStoreTest.java @@ -0,0 +1,146 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.unittests; + +import static org.junit.Assert.assertEquals; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; +import org.mockito.Mock; +import org.onap.music.exceptions.MusicQueryException; +import org.onap.music.exceptions.MusicServiceException; + +import org.onap.music.datastore.MusicDataStore; +import org.onap.music.datastore.PreparedQueryObject; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class MusicDataStoreTest { + + static MusicDataStore dataStore; + static PreparedQueryObject testObject; + + @BeforeClass + public static void init() { + dataStore = CassandraCQL.connectToEmbeddedCassandra(); + + } + + @AfterClass + public static void close() throws MusicServiceException, MusicQueryException { + + testObject = new PreparedQueryObject(); + testObject.appendQueryString(CassandraCQL.dropKeyspace); + dataStore.executePut(testObject, "eventual"); + dataStore.close(); + + } + + @Test + public void Test1_SetUp() throws MusicServiceException, MusicQueryException { + boolean result = false; + testObject = new PreparedQueryObject(); + testObject.appendQueryString(CassandraCQL.createKeySpace); + result = dataStore.executePut(testObject, "eventual");; + testObject = new PreparedQueryObject(); + testObject.appendQueryString(CassandraCQL.createTableEmployees); + result = dataStore.executePut(testObject, "eventual"); + assertEquals(true, result); + + } + + @Test + public void Test2_ExecutePut_eventual_insert() throws MusicServiceException, MusicQueryException { + testObject = CassandraCQL.setPreparedInsertQueryObject1(); + boolean result = dataStore.executePut(testObject, "eventual"); + assertEquals(true, result); + } + + @Test + public void Test3_ExecutePut_critical_insert() throws MusicServiceException, MusicQueryException { + testObject = CassandraCQL.setPreparedInsertQueryObject2(); + boolean result = dataStore.executePut(testObject, "Critical"); + assertEquals(true, result); + } + + @Test + public void Test4_ExecutePut_eventual_update() throws MusicServiceException, MusicQueryException { + testObject = CassandraCQL.setPreparedUpdateQueryObject(); + boolean result = false; + result = dataStore.executePut(testObject, "eventual"); + assertEquals(true, result); + } + + @Test + public void Test5_ExecuteEventualGet() throws MusicServiceException, MusicQueryException { + testObject = new PreparedQueryObject(); + testObject.appendQueryString(CassandraCQL.selectALL); + boolean result = false; + int count = 0; + ResultSet output = null; + output = dataStore.executeEventualGet(testObject); + System.out.println(output); + ; + for (Row row : output) { + count++; + System.out.println(row.toString()); + } + if (count == 2) { + result = true; + } + assertEquals(true, result); + } + + @Test + public void Test6_ExecuteCriticalGet() throws MusicServiceException, MusicQueryException { + testObject = CassandraCQL.setPreparedGetQuery(); + boolean result = false; + int count = 0; + ResultSet output = null; + output = dataStore.executeCriticalGet(testObject); + System.out.println(output); + ; + for (Row row : output) { + count++; + System.out.println(row.toString()); + } + if (count == 1) { + result = true; + } + assertEquals(true, result); + } + + @Test(expected = NullPointerException.class) + public void Test7_exception() { + PreparedQueryObject queryObject = null; + try { + dataStore.executePut(queryObject, "critical"); + } catch (MusicQueryException | MusicServiceException e) { + System.out.println(e.getMessage()); + } + + } +} diff --git a/src/test/java/org/onap/music/unittests/TestLockStore.java b/src/test/java/org/onap/music/unittests/TestLockStore.java new file mode 100644 index 00000000..4dbc7b4f --- /dev/null +++ b/src/test/java/org/onap/music/unittests/TestLockStore.java @@ -0,0 +1,53 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.unittests; + +import org.apache.log4j.Logger; +import org.onap.music.lockingservice.MusicLockingService; + +public class TestLockStore { + final static Logger logger = Logger.getLogger(TestLockStore.class); + + public static void main(String[] args) throws Exception { + String lockName = "/achristmkllas"; + MusicLockingService ml = new MusicLockingService(); + ml.deleteLock(lockName); + + + logger.info("lockname:" + lockName); + + String lockId1 = ml.createLockId(lockName); + logger.info("lockId1 " + lockId1); + logger.info(ml.isMyTurn(lockId1)); + + String lockId2 = ml.createLockId(lockName); + logger.info("lockId2 " + lockId2); + logger.info("check " + ml.isMyTurn("$bank$x-94608776321630264-0000000000")); + logger.info(ml.isMyTurn(lockId2)); + + // zkClient.unlock(lockId1); + // logger.info(ml.lock(lockId2)); + // zkClient.unlock(lockId2); + } + + +} diff --git a/src/test/java/org/onap/music/unittests/TestMusicCore.java b/src/test/java/org/onap/music/unittests/TestMusicCore.java new file mode 100644 index 00000000..17f911ce --- /dev/null +++ b/src/test/java/org/onap/music/unittests/TestMusicCore.java @@ -0,0 +1,445 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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============================================= + * ==================================================================== + */ +package org.onap.music.unittests; + +import static org.junit.Assert.*; +import static org.onap.music.main.MusicCore.mDstoreHandle; +import static org.onap.music.main.MusicCore.mLockHandle; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; +import org.onap.music.exceptions.MusicQueryException; +import org.onap.music.exceptions.MusicServiceException; +import org.onap.music.lockingservice.MusicLockState; +import org.onap.music.lockingservice.MusicLockingService; +import org.onap.music.lockingservice.MusicLockState.LockStatus; +import org.onap.music.main.MusicCore; +import org.onap.music.main.ResultType; +import org.onap.music.main.ReturnType; +import org.onap.music.main.MusicCore.Condition; +import org.onap.music.datastore.MusicDataStore; +import org.onap.music.datastore.PreparedQueryObject; +import com.datastax.driver.core.ResultSet; + +@RunWith(MockitoJUnitRunner.class) +public class TestMusicCore { + + @Mock + private Condition condition; + + @Mock + private ResultSet rs; + + @Mock + private PreparedQueryObject preparedQueryObject; + + @Before + public void setUp() { + mLockHandle = Mockito.mock(MusicLockingService.class); + + } + + @Test + public void testCreateLockReferenceforvalidlock() { + Mockito.when(mLockHandle.createLockId("/" + "test")).thenReturn("lock"); + String lockId = MusicCore.createLockReference("test"); + assertEquals("lock", lockId); + Mockito.verify(mLockHandle).createLockId("/" + "test"); + } + + @Test + public void testIsTableOrKeySpaceLock() { + Boolean result = MusicCore.isTableOrKeySpaceLock("ks1.tn1"); + assertTrue(result); + } + + @Test + public void testIsTableOrKeySpaceLockwithPrimarykey() { + Boolean result = MusicCore.isTableOrKeySpaceLock("ks1.tn1.pk1"); + assertFalse(result); + } + + @Test + public void testGetMusicLockState() { + MusicLockState musicLockState = new MusicLockState(LockStatus.UNLOCKED, "id1"); + Mockito.when(mLockHandle.getLockState("ks1.tb1.pk1")).thenReturn(musicLockState); + MusicLockState mls = MusicCore.getMusicLockState("ks1.tb1.pk1"); + assertEquals(musicLockState, mls); + Mockito.verify(mLockHandle).getLockState("ks1.tb1.pk1"); + } + + @Test + public void testAcquireLockifisMyTurnTrue() { + Mockito.when(mLockHandle.isMyTurn("id1")).thenReturn(true); + Boolean lock = MusicCore.acquireLock("ks1.tn1", "id1"); + assertTrue(lock); + Mockito.verify(mLockHandle).isMyTurn("id1"); + } + + @Test + public void testAcquireLockifisMyTurnFalse() { + Mockito.when(mLockHandle.isMyTurn("id1")).thenReturn(false); + Boolean lock = MusicCore.acquireLock("ks1.ts1", "id1"); + assertFalse(lock); + Mockito.verify(mLockHandle).isMyTurn("id1"); + } + + @Test + public void testAcquireLockifisMyTurnTrueandIsTableOrKeySpaceLockTrue() { + Mockito.when(mLockHandle.isMyTurn("id1")).thenReturn(true); + Boolean lock = MusicCore.acquireLock("ks1.tn1", "id1"); + assertTrue(lock); + Mockito.verify(mLockHandle).isMyTurn("id1"); + } + + @Test + public void testAcquireLockifisMyTurnTrueandIsTableOrKeySpaceLockFalseandHaveLock() { + MusicLockState musicLockState = new MusicLockState(LockStatus.LOCKED, "id1"); + Mockito.when(mLockHandle.isMyTurn("id1")).thenReturn(true); + Mockito.when(mLockHandle.getLockState("ks1.tn1.pk1")).thenReturn(musicLockState); + Boolean lock = MusicCore.acquireLock("ks1.tn1.pk1", "id1"); + assertTrue(lock); + Mockito.verify(mLockHandle).isMyTurn("id1"); + Mockito.verify(mLockHandle).getLockState("ks1.tn1.pk1"); + } + + @Test + public void testAcquireLockifisMyTurnTrueandIsTableOrKeySpaceLockFalseandDontHaveLock() { + MusicLockState musicLockState = new MusicLockState(LockStatus.LOCKED, "id2"); + Mockito.when(mLockHandle.isMyTurn("id1")).thenReturn(true); + Mockito.when(mLockHandle.getLockState("ks1.tn1.pk1")).thenReturn(musicLockState); + Boolean lock = MusicCore.acquireLock("ks1.tn1.pk1", "id1"); + assertTrue(lock); + Mockito.verify(mLockHandle).isMyTurn("id1"); + Mockito.verify(mLockHandle).getLockState("ks1.tn1.pk1"); + } + + @Test + public void testAcquireLockWithLeasewithLockStatusLOCKED() { + MusicLockState musicLockState = new MusicLockState(LockStatus.LOCKED, "id1"); + ReturnType expectedResult = new ReturnType(ResultType.SUCCESS, "Succes"); + Mockito.when(mLockHandle.getLockState("ks1.tn1.pk1")).thenReturn(musicLockState); + Mockito.when(mLockHandle.isMyTurn("id1")).thenReturn(true); + ReturnType actualResult = MusicCore.acquireLockWithLease("ks1.tn1.pk1", "id1", 6000); + assertEquals(expectedResult.getResult(), actualResult.getResult()); + Mockito.verify(mLockHandle).isMyTurn("id1"); + Mockito.verify(mLockHandle, Mockito.atLeastOnce()).getLockState("ks1.tn1.pk1"); + } + + @Test + public void testAcquireLockWithLeasewithLockStatusUNLOCKED() { + MusicLockState musicLockState = new MusicLockState(LockStatus.UNLOCKED, "id1"); + ReturnType expectedResult = new ReturnType(ResultType.SUCCESS, "Succes"); + Mockito.when(mLockHandle.getLockState("ks1.tn1.pk1")).thenReturn(musicLockState); + Mockito.when(mLockHandle.isMyTurn("id1")).thenReturn(true); + ReturnType actualResult = MusicCore.acquireLockWithLease("ks1.tn1.pk1", "id1", 6000); + assertEquals(expectedResult.getResult(), actualResult.getResult()); + Mockito.verify(mLockHandle).isMyTurn("id1"); + Mockito.verify(mLockHandle, Mockito.atLeastOnce()).getLockState("ks1.tn1.pk1"); + + } + + @Test + public void testAcquireLockWithLeaseIfNotMyTurn() { + MusicLockState musicLockState = new MusicLockState(LockStatus.UNLOCKED, "id1"); + ReturnType expectedResult = new ReturnType(ResultType.FAILURE, "Failure"); + Mockito.when(mLockHandle.getLockState("ks1.tn1.pk1")).thenReturn(musicLockState); + Mockito.when(mLockHandle.isMyTurn("id1")).thenReturn(false); + ReturnType actualResult = MusicCore.acquireLockWithLease("ks1.tn1.pk1", "id1", 6000); + assertEquals(expectedResult.getResult(), actualResult.getResult()); + Mockito.verify(mLockHandle).isMyTurn("id1"); + Mockito.verify(mLockHandle).getLockState("ks1.tn1.pk1"); + } + + /* + * @Test public void testQuorumGet() { mDstoreHandle = Mockito.mock(MusicDataStore.class); rs = + * Mockito.mock(ResultSet.class); + * Mockito.when(mDstoreHandle.executeCriticalGet("qu1")).thenReturn(rs); ResultSet rs1 = + * MusicCore.quorumGet("qu1"); assertNotNull(rs1); + * Mockito.verify(mDstoreHandle).executeCriticalGet("qu1"); + * + * } + */ + + @Test + public void testGetLockNameFromId() { + String lockname = MusicCore.getLockNameFromId("lockName$id"); + assertEquals("lockName", lockname); + } + + @Test + public void testDestroyLockRef() { + Mockito.doNothing().when(mLockHandle).unlockAndDeleteId("id1"); + MusicCore.destroyLockRef("id1"); + Mockito.verify(mLockHandle, Mockito.atLeastOnce()).unlockAndDeleteId("id1"); + } + + @Test + public void testreleaseLockwithvoluntaryReleaseTrue() { + MusicLockState musicLockState = new MusicLockState(LockStatus.UNLOCKED, "id2"); + Mockito.doNothing().when(mLockHandle).unlockAndDeleteId("id1"); + MusicLockState musicLockState1 = MusicCore.releaseLock("id1", true); + assertEquals(musicLockState.getLockStatus(), musicLockState1.getLockStatus()); + Mockito.verify(mLockHandle, Mockito.atLeastOnce()).unlockAndDeleteId("id1"); + } + + @Test + public void testreleaseLockwithvoluntaryReleaseFalse() { + MusicLockState musicLockState = new MusicLockState(LockStatus.UNLOCKED, "id2"); + Mockito.doNothing().when(mLockHandle).unlockAndDeleteId("id1"); + MusicLockState musicLockState1 = MusicCore.releaseLock("id1", false); + assertEquals(musicLockState.getLockStatus(), musicLockState1.getLockStatus()); + Mockito.verify(mLockHandle, Mockito.atLeastOnce()).unlockAndDeleteId("id1"); + } + + @Test + public void testDeleteLock() { + Mockito.doNothing().when(mLockHandle).deleteLock("/" + "id1"); + MusicCore.deleteLock("id1"); + Mockito.verify(mLockHandle).deleteLock("/" + "id1"); + } + + /* + * @Test public void testNonKeyRelatedPut() throws Exception { mDstoreHandle = + * Mockito.mock(MusicDataStore.class); Mockito.when(mDstoreHandle.executePut("qu1", + * "consistency")).thenReturn(true); Boolean result = MusicCore.nonKeyRelatedPut("qu1", + * "consistency"); assertTrue(result); Mockito.verify(mDstoreHandle).executePut("qu1", + * "consistency"); } + */ + + @Test + public void testEventualPutPreparedQuery() throws MusicServiceException, MusicQueryException { + mDstoreHandle = Mockito.mock(MusicDataStore.class); + preparedQueryObject = Mockito.mock(PreparedQueryObject.class); + ReturnType expectedResult = new ReturnType(ResultType.SUCCESS, "Succes"); + Mockito.when(mDstoreHandle.executePut(preparedQueryObject, "eventual")).thenReturn(true); + ReturnType actualResult = MusicCore.eventualPut(preparedQueryObject); + assertEquals(expectedResult.getResult(), actualResult.getResult()); + Mockito.verify(mDstoreHandle).executePut(preparedQueryObject, "eventual"); + } + + @Test + public void testEventualPutPreparedQuerywithResultFalse() + throws MusicServiceException, MusicQueryException { + mDstoreHandle = Mockito.mock(MusicDataStore.class); + preparedQueryObject = Mockito.mock(PreparedQueryObject.class); + ReturnType expectedResult = new ReturnType(ResultType.FAILURE, "Failure"); + Mockito.when(mDstoreHandle.executePut(preparedQueryObject, "eventual")).thenReturn(false); + ReturnType actualResult = MusicCore.eventualPut(preparedQueryObject); + assertEquals(expectedResult.getResult(), actualResult.getResult()); + Mockito.verify(mDstoreHandle).executePut(preparedQueryObject, "eventual"); + } + + @Test + public void testCriticalPutPreparedQuerywithValidLockId() + throws MusicServiceException, MusicQueryException { + mDstoreHandle = Mockito.mock(MusicDataStore.class); + preparedQueryObject = Mockito.mock(PreparedQueryObject.class); + MusicLockState musicLockState = new MusicLockState(LockStatus.UNLOCKED, "id1"); + Mockito.when(condition.testCondition()).thenReturn(true); + ReturnType expectedResult = new ReturnType(ResultType.SUCCESS, "Succes"); + Mockito.when(mLockHandle.getLockState("ks1" + "." + "tn1" + "." + "pk1")) + .thenReturn(musicLockState); + Mockito.when(mDstoreHandle.executePut(preparedQueryObject, "critical")).thenReturn(true); + ReturnType returnType = MusicCore.criticalPut("ks1", "tn1", "pk1", preparedQueryObject, + "id1", condition); + assertEquals(expectedResult.getResult(), returnType.getResult()); + Mockito.verify(condition).testCondition(); + Mockito.verify(mLockHandle).getLockState("ks1" + "." + "tn1" + "." + "pk1"); + Mockito.verify(mDstoreHandle).executePut(preparedQueryObject, "critical"); + } + + @Test + public void testCriticalPutPreparedQuerywithInvalidLockId() { + mDstoreHandle = Mockito.mock(MusicDataStore.class); + preparedQueryObject = Mockito.mock(PreparedQueryObject.class); + MusicLockState musicLockState = new MusicLockState(LockStatus.UNLOCKED, "id2"); + ReturnType expectedResult = new ReturnType(ResultType.FAILURE, "Failure"); + Mockito.when(mLockHandle.getLockState("ks1" + "." + "tn1" + "." + "pk1")) + .thenReturn(musicLockState); + ReturnType returnType = MusicCore.criticalPut("ks1", "tn1", "pk1", preparedQueryObject, + "id1", condition); + assertEquals(expectedResult.getResult(), returnType.getResult()); + Mockito.verify(mLockHandle).getLockState("ks1" + "." + "tn1" + "." + "pk1"); + } + + @Test + public void testCriticalPutPreparedQuerywithvalidLockIdandTestConditionFalse() { + mDstoreHandle = Mockito.mock(MusicDataStore.class); + preparedQueryObject = Mockito.mock(PreparedQueryObject.class); + MusicLockState musicLockState = new MusicLockState(LockStatus.UNLOCKED, "id1"); + Mockito.when(condition.testCondition()).thenReturn(false); + ReturnType expectedResult = new ReturnType(ResultType.FAILURE, "Failure"); + Mockito.when(mLockHandle.getLockState("ks1" + "." + "tn1" + "." + "pk1")) + .thenReturn(musicLockState); + ReturnType returnType = MusicCore.criticalPut("ks1", "tn1", "pk1", preparedQueryObject, + "id1", condition); + assertEquals(expectedResult.getResult(), returnType.getResult()); + Mockito.verify(condition).testCondition(); + Mockito.verify(mLockHandle).getLockState("ks1" + "." + "tn1" + "." + "pk1"); + } + + @Test + public void testNonKeyRelatedPutPreparedQuery() throws Exception { + mDstoreHandle = Mockito.mock(MusicDataStore.class); + preparedQueryObject = Mockito.mock(PreparedQueryObject.class); + Mockito.when(mDstoreHandle.executePut(preparedQueryObject, "consistency")).thenReturn(true); + Boolean result = MusicCore.nonKeyRelatedPut(preparedQueryObject, "consistency"); + assertTrue(result); + Mockito.verify(mDstoreHandle).executePut(preparedQueryObject, "consistency"); + } + + @Test + public void testAtomicPutPreparedQuery() throws MusicServiceException, MusicQueryException { + mDstoreHandle = Mockito.mock(MusicDataStore.class); + preparedQueryObject = Mockito.mock(PreparedQueryObject.class); + Mockito.when(mLockHandle.createLockId("/" + "ks1.tn1.pk1")).thenReturn("id1"); + MusicLockState musicLockState = new MusicLockState(LockStatus.LOCKED, "id1"); + ReturnType expectedResult = new ReturnType(ResultType.SUCCESS, "Succes"); + Mockito.when(mLockHandle.getLockState("ks1.tn1.pk1")).thenReturn(musicLockState); + Mockito.when(mLockHandle.isMyTurn("id1")).thenReturn(true); + Mockito.when(condition.testCondition()).thenReturn(true); + Mockito.when(mLockHandle.getLockState("ks1" + "." + "tn1" + "." + "pk1")) + .thenReturn(musicLockState); + Mockito.when(mDstoreHandle.executePut(preparedQueryObject, "critical")).thenReturn(true); + ReturnType returnType = + MusicCore.atomicPut("ks1", "tn1", "pk1", preparedQueryObject, condition); + assertEquals(expectedResult.getResult(), returnType.getResult()); + Mockito.verify(mLockHandle).createLockId("/" + "ks1.tn1.pk1"); + Mockito.verify(mLockHandle, Mockito.atLeastOnce()).getLockState("ks1.tn1.pk1"); + Mockito.verify(mLockHandle).isMyTurn("id1"); + Mockito.verify(condition).testCondition(); + Mockito.verify(mLockHandle, Mockito.atLeastOnce()) + .getLockState("ks1" + "." + "tn1" + "." + "pk1"); + Mockito.verify(mDstoreHandle).executePut(preparedQueryObject, "critical"); + } + + @Test + public void testAtomicPutPreparedQuerywithAcquireLockWithLeaseFalse() { + mDstoreHandle = Mockito.mock(MusicDataStore.class); + preparedQueryObject = Mockito.mock(PreparedQueryObject.class); + Mockito.when(mLockHandle.createLockId("/" + "ks1.tn1.pk1")).thenReturn("id1"); + MusicLockState musicLockState = new MusicLockState(LockStatus.LOCKED, "id1"); + ReturnType expectedResult = new ReturnType(ResultType.FAILURE, "Failure"); + Mockito.when(mLockHandle.getLockState("ks1.tn1.pk1")).thenReturn(musicLockState); + Mockito.when(mLockHandle.isMyTurn("id1")).thenReturn(false); + Mockito.when(mLockHandle.getLockState("ks1" + "." + "tn1" + "." + "pk1")) + .thenReturn(musicLockState); + ReturnType returnType = + MusicCore.atomicPut("ks1", "tn1", "pk1", preparedQueryObject, condition); + assertEquals(expectedResult.getResult(), returnType.getResult()); + Mockito.verify(mLockHandle, Mockito.atLeastOnce()).getLockState("ks1.tn1.pk1"); + Mockito.verify(mLockHandle).isMyTurn("id1"); + Mockito.verify(mLockHandle, Mockito.atLeastOnce()) + .getLockState("ks1" + "." + "tn1" + "." + "pk1"); + Mockito.verify(mLockHandle).createLockId("/" + "ks1.tn1.pk1"); + } + + @Test + public void testAtomicGetPreparedQuery() throws MusicServiceException, MusicQueryException { + mDstoreHandle = Mockito.mock(MusicDataStore.class); + preparedQueryObject = Mockito.mock(PreparedQueryObject.class); + rs = Mockito.mock(ResultSet.class); + Mockito.when(mLockHandle.createLockId("/" + "ks1.tn1.pk1")).thenReturn("id1"); + MusicLockState musicLockState = new MusicLockState(LockStatus.LOCKED, "id1"); + Mockito.when(mLockHandle.getLockState("ks1.tn1.pk1")).thenReturn(musicLockState); + Mockito.when(mLockHandle.isMyTurn("id1")).thenReturn(true); + Mockito.when(mLockHandle.getLockState("ks1" + "." + "tn1" + "." + "pk1")) + .thenReturn(musicLockState); + Mockito.when(mDstoreHandle.executeCriticalGet(preparedQueryObject)).thenReturn(rs); + ResultSet rs1 = MusicCore.atomicGet("ks1", "tn1", "pk1", preparedQueryObject); + assertNotNull(rs1); + Mockito.verify(mLockHandle).createLockId("/" + "ks1.tn1.pk1"); + Mockito.verify(mLockHandle, Mockito.atLeastOnce()).getLockState("ks1.tn1.pk1"); + Mockito.verify(mLockHandle).isMyTurn("id1"); + Mockito.verify(mLockHandle, Mockito.atLeastOnce()) + .getLockState("ks1" + "." + "tn1" + "." + "pk1"); + Mockito.verify(mDstoreHandle).executeCriticalGet(preparedQueryObject); + } + + @Test + public void testAtomicGetPreparedQuerywithAcquireLockWithLeaseFalse() + throws MusicServiceException { + mDstoreHandle = Mockito.mock(MusicDataStore.class); + preparedQueryObject = Mockito.mock(PreparedQueryObject.class); + rs = Mockito.mock(ResultSet.class); + Mockito.when(mLockHandle.createLockId("/" + "ks1.tn1.pk1")).thenReturn("id1"); + MusicLockState musicLockState = new MusicLockState(LockStatus.LOCKED, "id1"); + Mockito.when(mLockHandle.getLockState("ks1.tn1.pk1")).thenReturn(musicLockState); + Mockito.when(mLockHandle.isMyTurn("id1")).thenReturn(false); + Mockito.when(mLockHandle.getLockState("ks1" + "." + "tn1" + "." + "pk1")) + .thenReturn(musicLockState); + ResultSet rs1 = MusicCore.atomicGet("ks1", "tn1", "pk1", preparedQueryObject); + assertNull(rs1); + Mockito.verify(mLockHandle).createLockId("/" + "ks1.tn1.pk1"); + Mockito.verify(mLockHandle, Mockito.atLeastOnce()).getLockState("ks1.tn1.pk1"); + Mockito.verify(mLockHandle).isMyTurn("id1"); + Mockito.verify(mLockHandle, Mockito.atLeastOnce()) + .getLockState("ks1" + "." + "tn1" + "." + "pk1"); + + } + + @Test + public void testGetPreparedQuery() throws MusicServiceException, MusicQueryException { + mDstoreHandle = Mockito.mock(MusicDataStore.class); + rs = Mockito.mock(ResultSet.class); + preparedQueryObject = Mockito.mock(PreparedQueryObject.class); + Mockito.when(mDstoreHandle.executeEventualGet(preparedQueryObject)).thenReturn(rs); + ResultSet rs1 = MusicCore.get(preparedQueryObject); + assertNotNull(rs1); + Mockito.verify(mDstoreHandle).executeEventualGet(preparedQueryObject); + + } + + @Test + public void testcriticalGetPreparedQuery() throws MusicServiceException, MusicQueryException { + mDstoreHandle = Mockito.mock(MusicDataStore.class); + preparedQueryObject = Mockito.mock(PreparedQueryObject.class); + MusicLockState musicLockState = new MusicLockState(LockStatus.UNLOCKED, "id1"); + rs = Mockito.mock(ResultSet.class); + Mockito.when(mLockHandle.getLockState("ks1" + "." + "tn1" + "." + "pk1")) + .thenReturn(musicLockState); + Mockito.when(mDstoreHandle.executeCriticalGet(preparedQueryObject)).thenReturn(rs); + ResultSet rs1 = MusicCore.criticalGet("ks1", "tn1", "pk1", preparedQueryObject, "id1"); + assertNotNull(rs1); + Mockito.verify(mLockHandle, Mockito.atLeastOnce()) + .getLockState("ks1" + "." + "tn1" + "." + "pk1"); + Mockito.verify(mDstoreHandle).executeCriticalGet(preparedQueryObject); + } + + @Test + public void testcriticalGetPreparedQuerywithInvalidLockId() throws MusicServiceException { + mDstoreHandle = Mockito.mock(MusicDataStore.class); + preparedQueryObject = Mockito.mock(PreparedQueryObject.class); + MusicLockState musicLockState = new MusicLockState(LockStatus.UNLOCKED, "id2"); + Mockito.when(mLockHandle.getLockState("ks1" + "." + "tn1" + "." + "pk1")) + .thenReturn(musicLockState); + ResultSet rs1 = MusicCore.criticalGet("ks1", "tn1", "pk1", preparedQueryObject, "id1"); + assertNull(rs1); + Mockito.verify(mLockHandle, Mockito.atLeastOnce()) + .getLockState("ks1" + "." + "tn1" + "." + "pk1"); + } + +} |