397783389b7e818deb91fcc6c1170d2844d6a76b
[music.git] / src / main / java / org / onap / music / rest / RestMusicDataAPI.java
1 /*
2  * ============LICENSE_START==========================================
3  * org.onap.music
4  * ===================================================================
5  *  Copyright (c) 2017 AT&T Intellectual Property
6  * ===================================================================
7  *  Modifications Copyright (c) 2019 Samsung
8  * ===================================================================
9  *  Licensed under the Apache License, Version 2.0 (the "License");
10  *  you may not use this file except in compliance with the License.
11  *  You may obtain a copy of the License at
12  *
13  *     http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *  Unless required by applicable law or agreed to in writing, software
16  *  distributed under the License is distributed on an "AS IS" BASIS,
17  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  *  See the License for the specific language governing permissions and
19  *  limitations under the License.
20  *
21  * ============LICENSE_END=============================================
22  * ====================================================================
23  */
24
25 package org.onap.music.rest;
26
27 import java.nio.ByteBuffer;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.UUID;
31
32 import javax.ws.rs.Consumes;
33 import javax.ws.rs.DELETE;
34 import javax.ws.rs.GET;
35 import javax.ws.rs.HeaderParam;
36 import javax.ws.rs.POST;
37 import javax.ws.rs.PUT;
38 import javax.ws.rs.Path;
39 import javax.ws.rs.PathParam;
40 import javax.ws.rs.Produces;
41 import javax.ws.rs.core.Context;
42 import javax.ws.rs.core.MediaType;
43 import javax.ws.rs.core.MultivaluedMap;
44 import javax.ws.rs.core.Response;
45 import javax.ws.rs.core.Response.ResponseBuilder;
46 import javax.ws.rs.core.Response.Status;
47 import javax.ws.rs.core.UriInfo;
48
49 import org.apache.commons.lang3.StringUtils;
50 import org.onap.music.datastore.PreparedQueryObject;
51 import org.onap.music.datastore.jsonobjects.JsonDelete;
52 import org.onap.music.datastore.jsonobjects.JsonInsert;
53 import org.onap.music.datastore.jsonobjects.JsonKeySpace;
54 import org.onap.music.datastore.jsonobjects.JsonTable;
55 import org.onap.music.datastore.jsonobjects.JsonUpdate;
56 import org.onap.music.eelf.logging.EELFLoggerDelegate;
57 import org.onap.music.exceptions.MusicLockingException;
58 import org.onap.music.exceptions.MusicQueryException;
59 import org.onap.music.eelf.logging.format.AppMessages;
60 import org.onap.music.eelf.logging.format.ErrorSeverity;
61 import org.onap.music.eelf.logging.format.ErrorTypes;
62 import org.onap.music.exceptions.MusicServiceException;
63 import org.onap.music.main.MusicCore;
64 import org.onap.music.datastore.Condition;
65 import org.onap.music.datastore.MusicDataStoreHandle;
66 import org.onap.music.main.MusicUtil;
67 import org.onap.music.main.ResultType;
68 import org.onap.music.main.ReturnType;
69 import org.onap.music.response.jsonobjects.JsonResponse;
70
71 import com.datastax.driver.core.DataType;
72 import com.datastax.driver.core.ResultSet;
73 import com.datastax.driver.core.TableMetadata;
74
75 import io.swagger.annotations.Api;
76 import io.swagger.annotations.ApiImplicitParam;
77 import io.swagger.annotations.ApiImplicitParams;
78 import io.swagger.annotations.ApiOperation;
79 import io.swagger.annotations.ApiParam;
80 import io.swagger.annotations.ApiResponses;
81 import io.swagger.annotations.ApiResponse;
82 import io.swagger.annotations.Example;
83 import io.swagger.annotations.ExampleProperty;
84
85 /* Version 2 Class */
86 //@Path("/v{version: [0-9]+}/keyspaces")
87 @Path("/v2/keyspaces")
88 @Api(value = "Data Api")
89 public class RestMusicDataAPI {
90     /*
91      * Header values for Versioning X-minorVersion *** - Used to request or communicate a MINOR
92      * version back from the client to the server, and from the server back to the client - This
93      * will be the MINOR version requested by the client, or the MINOR version of the last MAJOR
94      * version (if not specified by the client on the request) - Contains a single position value
95      * (e.g. if the full version is 1.24.5, X-minorVersion = "24") - Is optional for the client on
96      * request; however, this header should be provided if the client needs to take advantage of
97      * MINOR incremented version functionality - Is mandatory for the server on response
98      *
99      *** X-patchVersion *** - Used only to communicate a PATCH version in a response for
100      * troubleshooting purposes only, and will not be provided by the client on request - This will
101      * be the latest PATCH version of the MINOR requested by the client, or the latest PATCH version
102      * of the MAJOR (if not specified by the client on the request) - Contains a single position
103      * value (e.g. if the full version is 1.24.5, X-patchVersion = "5") - Is mandatory for the
104      * server on response  (CURRENTLY NOT USED)
105      *
106      *** X-latestVersion *** - Used only to communicate an API's latest version - Is mandatory for the
107      * server on response, and shall include the entire version of the API (e.g. if the full version
108      * is 1.24.5, X-latestVersion = "1.24.5") - Used in the response to inform clients that they are
109      * not using the latest version of the API (CURRENTLY NOT USED)
110      *
111      */
112
113     private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(RestMusicDataAPI.class);
114     private static final String XMINORVERSION = "X-minorVersion";
115     private static final String XPATCHVERSION = "X-patchVersion";
116     private static final String NS = "ns";
117     private static final String VERSION = "v2";
118     private static final String PARAMETER_ERROR = "Missing Row Identifier. Please provide the parameter of key=value for the row being selected.";
119
120
121     private class RowIdentifier {
122         public String primarKeyValue;
123         public StringBuilder rowIdString;
124         @SuppressWarnings("unused")
125         public PreparedQueryObject queryObject; // the string with all the row
126                                                 // identifiers separated by AND
127
128         public RowIdentifier(String primaryKeyValue, StringBuilder rowIdString,
129                         PreparedQueryObject queryObject) {
130             this.primarKeyValue = primaryKeyValue;
131             this.rowIdString = rowIdString;
132             this.queryObject = queryObject;
133         }
134     }
135
136
137     /**
138      * Create Keyspace REST
139      *
140      * @param kspObject
141      * @param keyspaceName
142      * @return
143      * @throws Exception
144      */
145     @POST
146     @Path("/{name}")
147     @ApiOperation(value = "Create Keyspace", response = String.class,
148         notes = "This API will not work if MUSIC properties has keyspace.active=false ")
149     @Consumes(MediaType.APPLICATION_JSON)
150     @Produces(MediaType.APPLICATION_JSON)
151     @ApiResponses(value={
152         @ApiResponse(code=200, message = "Success",examples = @Example( value =  {
153             @ExampleProperty(mediaType="application/json",value = 
154                 "{\"message\" : \"Keysapce <keyspace> Created\","
155                 + "\"status\" : \"SUCCESS\"}")
156         })),
157         @ApiResponse(code=400, message = "Failure",examples = @Example( value =  {
158             @ExampleProperty(mediaType="application/json",value = 
159                 "{\"error\" : \"<errorMessage>\","
160                 + "\"status\" : \"FAILURE\"}") 
161         }))
162     })  
163     public Response createKeySpace(
164         @ApiParam(value = "Major Version",required = true) @PathParam("version") String version,
165         @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion,
166         @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
167         @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid,
168         @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
169         @ApiParam(value = "Application namespace",required = false, hidden = true) @HeaderParam(NS) String ns,
170         JsonKeySpace kspObject,
171         @ApiParam(value = "Keyspace Name",required = true) @PathParam("name") String keyspaceName) {
172         try {
173             ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
174             EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspaceName+" ) ");
175             logger.info(EELFLoggerDelegate.applicationLogger,"In Create Keyspace " + keyspaceName);
176             if (MusicUtil.isKeyspaceActive() ) {
177                 logger.info(EELFLoggerDelegate.applicationLogger,"Creating Keyspace " + keyspaceName);
178                 
179                 if(kspObject == null || kspObject.getReplicationInfo() == null) {
180                     response.status(Status.BAD_REQUEST);
181                     return response.entity(new JsonResponse(ResultType.FAILURE).setError(ResultType.BODYMISSING.getResult()).toMap()).build();
182                 }
183         
184                 String consistency = MusicUtil.EVENTUAL;// for now this needs only eventual consistency
185         
186                 PreparedQueryObject queryObject = new PreparedQueryObject();
187                 if(consistency.equalsIgnoreCase(MusicUtil.EVENTUAL) && kspObject.getConsistencyInfo().get("consistency") != null) {
188                     if(MusicUtil.isValidConsistency(kspObject.getConsistencyInfo().get("consistency")))
189                         queryObject.setConsistency(kspObject.getConsistencyInfo().get("consistency"));
190                     else
191                         return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.SYNTAXERROR).setError("Invalid Consistency type").toMap()).build();
192                 }
193                 long start = System.currentTimeMillis();
194                 Map<String, Object> replicationInfo = kspObject.getReplicationInfo();
195                 String repString = null;
196                 try {
197                     repString = "{" + MusicUtil.jsonMaptoSqlString(replicationInfo, ",") + "}";
198                 } catch (Exception e) {
199                     logger.error(EELFLoggerDelegate.errorLogger,e.getMessage(), AppMessages.MISSINGDATA  ,ErrorSeverity
200                         .CRITICAL, ErrorTypes.DATAERROR, e);
201         
202                 }
203                 queryObject.appendQueryString(
204                                 "CREATE KEYSPACE " + keyspaceName + " WITH replication = " + repString);
205                 if (kspObject.getDurabilityOfWrites() != null) {
206                     queryObject.appendQueryString(
207                                     " AND durable_writes = " + kspObject.getDurabilityOfWrites());
208                 }
209         
210                 queryObject.appendQueryString(";");
211                 long end = System.currentTimeMillis();
212                 logger.info(EELFLoggerDelegate.applicationLogger,
213                                 "Time taken for setting up query in create keyspace:" + (end - start));
214         
215                 ResultType result = ResultType.FAILURE;
216                 try {
217                     result = MusicCore.nonKeyRelatedPut(queryObject, consistency);
218                 } catch ( MusicQueryException ex) {
219                     logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.QUERYERROR 
220                     ,ErrorSeverity.WARN, ErrorTypes.QUERYERROR);
221                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
222                 } catch ( MusicServiceException ex) {
223                     logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.UNKNOWNERROR  ,ErrorSeverity
224                         .WARN, ErrorTypes.MUSICSERVICEERROR, ex);
225                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
226                 }
227         
228                 return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setMessage("Keyspace " + keyspaceName + " Created").toMap()).build();
229             } else {
230                 String vError = "Keyspace Creation has been turned off. Contact DBA to create the keyspace or set keyspace.active to true.";
231                 logger.info(EELFLoggerDelegate.applicationLogger,vError);
232                 logger.error(EELFLoggerDelegate.errorLogger,vError, AppMessages.UNKNOWNERROR,ErrorSeverity.WARN, ErrorTypes.MUSICSERVICEERROR);
233                 return response.status(Response.Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(vError).toMap()).build();
234             }
235         } finally {
236             EELFLoggerDelegate.mdcRemove("keyspace");
237         }
238         
239     }
240
241     /**
242      *
243      * @param kspObject
244      * @param keyspaceName
245      * @return
246      * @throws Exception
247      */
248     @DELETE
249     @Path("/{name}")
250     @ApiOperation(value = "Delete Keyspace", response = String.class,
251         notes = "This API will not work if MUSIC properties has keyspace.active=false ")
252     @Produces(MediaType.APPLICATION_JSON)
253     @ApiResponses(value={
254         @ApiResponse(code=200, message = "Success",examples = @Example( value =  {
255             @ExampleProperty(mediaType="application/json",value = 
256                 "{\"message\" : \"Keysapce <keyspace> Deleted\","
257                 + "\"status\" : \"SUCCESS\"}")
258         })),
259         @ApiResponse(code=400, message = "Failure",examples = @Example( value =  {
260             @ExampleProperty(mediaType="application/json",value = 
261                 "{\"error\" : \"<errorMessage>\","
262                 + "\"status\" : \"FAILURE\"}") 
263         }))
264     })  
265     public Response dropKeySpace(
266         @ApiParam(value = "Major Version",required = true) @PathParam("version") String version,
267         @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion,
268         @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
269         @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid,
270         @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
271         @ApiParam(value = "Application namespace",required = false, hidden = true) @HeaderParam(NS) String ns,
272         @ApiParam(value = "Keyspace Name",required = true) @PathParam("name") String keyspaceName) throws Exception {
273         try {
274             ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
275             EELFLoggerDelegate.mdcPut("keyspace", "( " + keyspaceName + " ) ");
276             logger.info(EELFLoggerDelegate.applicationLogger,"In Drop Keyspace " + keyspaceName);
277             if (MusicUtil.isKeyspaceActive()) {
278                 String consistency = MusicUtil.EVENTUAL;// for now this needs only
279                 PreparedQueryObject queryObject = new PreparedQueryObject();
280                 queryObject.appendQueryString("DROP KEYSPACE " + keyspaceName + ";");
281                 String droperror = "Error Deleteing Keyspace " + keyspaceName;
282                 try{
283                     ResultType result = MusicCore.nonKeyRelatedPut(queryObject, consistency);
284                     if ( result.equals(ResultType.FAILURE) ) {
285                         return response.status(Status.BAD_REQUEST).entity(new JsonResponse(result).setError(droperror).toMap()).build();
286                     }
287                 } catch ( MusicQueryException ex) {
288                     logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.QUERYERROR 
289                     ,ErrorSeverity.WARN, ErrorTypes.QUERYERROR);
290                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(droperror + " " + ex.getMessage()).toMap()).build();
291                 } catch ( MusicServiceException ex) {
292                     logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.UNKNOWNERROR
293                         ,ErrorSeverity.WARN, ErrorTypes.MUSICSERVICEERROR, ex);
294                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(droperror + " " + ex.getMessage()).toMap()).build();
295                 }
296                 return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setMessage("Keyspace " + keyspaceName + " Deleted").toMap()).build();
297             } else {
298                 String vError = "Keyspace deletion has been turned off. Contact DBA to delete the keyspace or set keyspace.active to true.";
299                 logger.info(EELFLoggerDelegate.applicationLogger,vError);
300                 logger.error(EELFLoggerDelegate.errorLogger,vError, AppMessages.UNKNOWNERROR,ErrorSeverity.WARN, ErrorTypes.MUSICSERVICEERROR);
301                 return response.status(Response.Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(vError).toMap()).build();
302             }
303         } finally {
304             EELFLoggerDelegate.mdcRemove("keyspace");
305         }
306     }
307
308     /**
309      *
310      * @param tableObj
311      * @param version
312      * @param keyspace
313      * @param tablename
314      * @param headers
315      * @return
316      * @throws Exception -
317      */
318     @POST
319     @Path("/{keyspace: .*}/tables/{tablename: .*}")
320     @ApiOperation(value = "Create Table", response = String.class,
321         notes = "Create a table with the required json in the body.")
322     @Consumes(MediaType.APPLICATION_JSON)
323     @Produces(MediaType.APPLICATION_JSON)
324     @ApiResponses(value={
325         @ApiResponse(code=200, message = "Success",examples = @Example( value =  {
326             @ExampleProperty(mediaType="application/json",value = 
327                 "{\"message\" : \"Tablename <tablename> Created under keyspace <keyspace>\","
328                 + "\"status\" : \"SUCCESS\"}")
329         })),
330         @ApiResponse(code=400, message = "Failure",examples = @Example( value =  {
331             @ExampleProperty(mediaType="application/json",value = 
332                 "{\"error\" : \"<errorMessage>\","
333                 + "\"status\" : \"FAILURE\"}") 
334         }))
335     })  
336     public Response createTable(
337         @ApiParam(value = "Major Version",required = true) @PathParam("version") String version,
338         @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion,
339         @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
340         @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid,
341         @ApiParam(value = "Application namespace",required = false, hidden = true) @HeaderParam(NS) String ns,
342         @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
343         JsonTable tableObj,
344         @ApiParam(value = "Keyspace Name",required = true) @PathParam("keyspace") String keyspace,
345         @ApiParam(value = "Table Name",required = true) @PathParam("tablename") String tablename) throws Exception {
346         try {
347             ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
348             if ( null == tableObj ) {
349                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
350                         .setError(ResultType.BODYMISSING.getResult()).toMap()).build();
351             }
352             if(keyspace == null || keyspace.isEmpty() || tablename == null || tablename.isEmpty()){
353                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
354                         .setError("One or more path parameters are not set, please check and try again."
355                             + "Parameter values: keyspace='" + keyspace + "' tablename='" + tablename + "'")
356                             .toMap()).build();
357             }
358             EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) ");
359             String consistency = MusicUtil.EVENTUAL;
360             // for now this needs only eventual consistency
361             String primaryKey = null;
362             String partitionKey = tableObj.getPartitionKey();
363             String clusterKey = tableObj.getClusteringKey();
364             String filteringKey = tableObj.getFilteringKey();
365             if(filteringKey != null) {
366                 clusterKey = clusterKey + "," + filteringKey;
367             }
368             primaryKey = tableObj.getPrimaryKey(); // get primaryKey if available
369
370             PreparedQueryObject queryObject = new PreparedQueryObject();
371             // first read the information about the table fields
372             Map<String, String> fields = tableObj.getFields();
373             if (fields == null) {
374                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
375                     .setError("Create Table Error: No fields in request").toMap()).build();
376             }
377
378             StringBuilder fieldsString = new StringBuilder("(vector_ts text,");
379             int counter = 0;
380             for (Map.Entry<String, String> entry : fields.entrySet()) {
381                 if (entry.getKey().equals("PRIMARY KEY")) {
382                     primaryKey = entry.getValue(); // replaces primaryKey
383                     primaryKey = primaryKey.trim();
384                 } else {
385                     if (counter == 0 )  fieldsString.append("" + entry.getKey() + " " + entry.getValue() + "");
386                     else fieldsString.append("," + entry.getKey() + " " + entry.getValue() + "");
387                 }
388
389                 if (counter != (fields.size() - 1) ) {
390                     counter = counter + 1; 
391                 } else {
392             
393                     if((primaryKey != null) && (partitionKey == null)) {
394                         primaryKey = primaryKey.trim();
395                         int count1 = StringUtils.countMatches(primaryKey, ')');
396                         int count2 = StringUtils.countMatches(primaryKey, '(');
397                         if (count1 != count2) {
398                             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
399                                 .setError("Create Table Error: primary key '(' and ')' do not match, primary key=" + primaryKey)
400                                 .toMap()).build();
401                         }
402
403                         if ( primaryKey.indexOf('(') == -1  || ( count2 == 1 && (primaryKey.lastIndexOf(')') +1) ==  primaryKey.length() ) ) {
404                             if (primaryKey.contains(",") ) {
405                                 partitionKey= primaryKey.substring(0,primaryKey.indexOf(','));
406                                 partitionKey=partitionKey.replaceAll("[\\(]+","");
407                                 clusterKey=primaryKey.substring(primaryKey.indexOf(',')+1);  // make sure index
408                                 clusterKey=clusterKey.replaceAll("[)]+", "");
409                             } else {
410                                 partitionKey=primaryKey;
411                                 partitionKey=partitionKey.replaceAll("[\\)]+","");
412                                 partitionKey=partitionKey.replaceAll("[\\(]+","");
413                                 clusterKey="";
414                             }
415                         } else {   // not null and has ) before the last char
416                             partitionKey= primaryKey.substring(0,primaryKey.indexOf(')'));
417                             partitionKey=partitionKey.replaceAll("[\\(]+","");
418                             partitionKey = partitionKey.trim();
419                             clusterKey= primaryKey.substring(primaryKey.indexOf(')'));
420                             clusterKey=clusterKey.replaceAll("[\\(]+","");
421                             clusterKey=clusterKey.replaceAll("[\\)]+","");
422                             clusterKey = clusterKey.trim();
423                             if (clusterKey.indexOf(',') == 0) {
424                                 clusterKey=clusterKey.substring(1);
425                             }
426                             clusterKey = clusterKey.trim();
427                             if (clusterKey.equals(",") ) clusterKey=""; // print error if needed    ( ... ),)
428                         }
429
430                         if (!(partitionKey.isEmpty() || clusterKey.isEmpty())
431                             && (partitionKey.equalsIgnoreCase(clusterKey) ||
432                             clusterKey.contains(partitionKey) || partitionKey.contains(clusterKey)) ) {
433                             logger.error("DataAPI createTable partition/cluster key ERROR: partitionKey="+partitionKey+", clusterKey=" + clusterKey + " and primary key=" + primaryKey );
434                             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(
435                                 "Create Table primary key error: clusterKey(" + clusterKey + ") equals/contains/overlaps partitionKey(" +partitionKey+ ")  of"
436                                 + " primary key=" + primaryKey)
437                                 .toMap()).build();
438
439                         }
440
441                         if (partitionKey.isEmpty() )  primaryKey="";
442                         else  if (clusterKey.isEmpty() ) primaryKey=" (" + partitionKey  + ")";
443                         else  primaryKey=" (" + partitionKey + ")," + clusterKey;
444
445                 
446                         if (primaryKey != null) fieldsString.append(", PRIMARY KEY (" + primaryKey + " )");
447
448                     } else { // end of length > 0
449                     
450                         if (!(partitionKey.isEmpty() || clusterKey.isEmpty())
451                             && (partitionKey.equalsIgnoreCase(clusterKey) ||
452                             clusterKey.contains(partitionKey) || partitionKey.contains(clusterKey)) ) {
453                             logger.error("DataAPI createTable partition/cluster key ERROR: partitionKey="+partitionKey+", clusterKey=" + clusterKey);
454                             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(
455                                 "Create Table primary key error: clusterKey(" + clusterKey + ") equals/contains/overlaps partitionKey(" +partitionKey+ ")")
456                                 .toMap()).build();
457                         }
458
459                         if (partitionKey.isEmpty() )  primaryKey="";
460                         else  if (clusterKey.isEmpty() ) primaryKey=" (" + partitionKey  + ")";
461                         else  primaryKey=" (" + partitionKey + ")," + clusterKey;
462
463                         if (primaryKey != null) fieldsString.append(", PRIMARY KEY (" + primaryKey + " )");
464                     }
465                     fieldsString.append(")");
466
467                 } // end of last field check
468
469             } // end of for each
470             // information about the name-value style properties
471             Map<String, Object> propertiesMap = tableObj.getProperties();
472             StringBuilder propertiesString = new StringBuilder();
473             if (propertiesMap != null) {
474                 counter = 0;
475                 for (Map.Entry<String, Object> entry : propertiesMap.entrySet()) {
476                     Object ot = entry.getValue();
477                     String value = ot + "";
478                     if (ot instanceof String) {
479                         value = "'" + value + "'";
480                     } else if (ot instanceof Map) {
481                         @SuppressWarnings("unchecked")
482                         Map<String, Object> otMap = (Map<String, Object>) ot;
483                         value = "{" + MusicUtil.jsonMaptoSqlString(otMap, ",") + "}";
484                     }
485
486                     propertiesString.append(entry.getKey() + "=" + value + "");
487                     if (counter != propertiesMap.size() - 1)
488                         propertiesString.append(" AND ");
489
490                     counter = counter + 1;
491                 }
492             }
493
494             String clusteringOrder = tableObj.getClusteringOrder();
495
496             if (clusteringOrder != null && !(clusteringOrder.isEmpty())) {
497                 String[] arrayClusterOrder = clusteringOrder.split("[,]+");
498
499                 for (int i = 0; i < arrayClusterOrder.length; i++) {
500                     String[] clusterS = arrayClusterOrder[i].trim().split("[ ]+");
501                     if ( (clusterS.length ==2)  && (clusterS[1].equalsIgnoreCase("ASC") || clusterS[1].equalsIgnoreCase("DESC"))) {
502                         continue;
503                     } else {
504                         return response.status(Status.BAD_REQUEST)
505                         .entity(new JsonResponse(ResultType.FAILURE)
506                         .setError("createTable/Clustering Order vlaue ERROR: valid clustering order is ASC or DESC or expecting colname  order; please correct clusteringOrder:"+ clusteringOrder+".")
507                         .toMap()).build();
508                     }
509                     // add validation for column names in cluster key
510                 }
511
512                 if (!(clusterKey.isEmpty())) {
513                     clusteringOrder = "CLUSTERING ORDER BY (" +clusteringOrder +")";
514                     //cjc check if propertiesString.length() >0 instead propertiesMap
515                     if (propertiesMap != null) {
516                         propertiesString.append(" AND  "+ clusteringOrder);
517                     } else {
518                         propertiesString.append(clusteringOrder);
519                     }
520                 } else {
521                     logger.warn("Skipping clustering order=("+clusteringOrder+ ") since clustering key is empty ");
522                 }
523             } //if non empty
524
525             queryObject.appendQueryString(
526                 "CREATE TABLE " + keyspace + "." + tablename + " " + fieldsString);
527
528
529             if (propertiesString != null &&  propertiesString.length()>0 )
530                 queryObject.appendQueryString(" WITH " + propertiesString);
531             queryObject.appendQueryString(";");
532             ResultType result = ResultType.FAILURE;
533             try {
534                 result = MusicCore.createTable(keyspace, tablename, queryObject, consistency);
535             } catch (MusicServiceException ex) {
536                 logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.UNKNOWNERROR  ,ErrorSeverity.CRITICAL, ErrorTypes.MUSICSERVICEERROR);
537                 response.status(Status.BAD_REQUEST);
538                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
539             }
540             if ( result.equals(ResultType.FAILURE) ) {
541                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(result).setError("Error Creating Table " + tablename).toMap()).build();
542             }
543             return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setMessage("TableName " + tablename.trim() + " Created under keyspace " + keyspace.trim()).toMap()).build();
544         } finally {
545             EELFLoggerDelegate.mdcRemove("keyspace");
546         }
547     }
548
549     /**
550      *
551      * @param keyspace
552      * @param tablename
553      * @param fieldName
554      * @param info
555      * @throws Exception
556      */
557     @POST
558     @Path("/{keyspace: .*}/tables/{tablename: .*}/index/{field: .*}")
559     @ApiOperation(value = "Create Index", response = String.class,
560         notes = "An index provides a means to access data using attributes "
561         + "other than the partition key. The benefit is fast, efficient lookup "
562         + "of data matching a given condition.")
563     @Produces(MediaType.APPLICATION_JSON)
564     @ApiResponses(value={
565         @ApiResponse(code=200, message = "Success",examples = @Example( value =  {
566             @ExampleProperty(mediaType="application/json",value = 
567                 "{\"message\" : \"Index Created on <keyspace>.<table>.<field>\","
568                 + "\"status\" : \"SUCCESS\"}")
569         })),
570         @ApiResponse(code=400, message = "Failure",examples = @Example( value =  {
571             @ExampleProperty(mediaType="application/json",value = 
572                 "{\"error\" : \"<errorMessage>\","
573                 + "\"status\" : \"FAILURE\"}") 
574         })),
575         @ApiResponse(code=400, message = "Failure",examples = @Example( value =  {
576             @ExampleProperty(mediaType="application/json",value = 
577                 "{\"error\" : \"Unknown Error in create index.\","
578                 + "\"status\" : \"FAILURE\"}") 
579         }))
580     })  
581     public Response createIndex(
582         @ApiParam(value = "Major Version",required = true) @PathParam("version") String version,
583         @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion,
584         @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
585         @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid,
586         @ApiParam(value = "Application namespace",required = false, hidden = true) @HeaderParam(NS) String ns,
587         @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
588         @ApiParam(value = "Keyspace Name",required = true) @PathParam("keyspace") String keyspace,
589         @ApiParam(value = "Table Name",required = true) @PathParam("tablename") String tablename,
590         @ApiParam(value = "Field Name",required = true) @PathParam("field") String fieldName,
591         @Context UriInfo info) throws Exception {
592         try {
593             ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
594             if ((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty()) || (fieldName == null || fieldName.isEmpty())){
595                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
596                     .setError("one or more path parameters are not set, please check and try again")
597                     .toMap()).build();
598             }
599             EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) ");
600             MultivaluedMap<String, String> rowParams = info.getQueryParameters();
601             String indexName = "";
602             if (rowParams.getFirst("index_name") != null)
603                 indexName = rowParams.getFirst("index_name");
604             PreparedQueryObject query = new PreparedQueryObject();
605             query.appendQueryString("Create index if not exists " + indexName + "  on " + keyspace + "."
606                             + tablename + " (" + fieldName + ");");
607
608             ResultType result = ResultType.FAILURE;
609             try {
610                 result = MusicCore.nonKeyRelatedPut(query, "eventual");
611             } catch (MusicServiceException ex) {
612                 logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.UNKNOWNERROR  ,ErrorSeverity
613                 .CRITICAL, ErrorTypes.GENERALSERVICEERROR, ex);
614                 response.status(Status.BAD_REQUEST);
615                 return response.entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
616             }
617             if ( result.equals(ResultType.SUCCESS) ) {
618                 return response.status(Status.OK).entity(new JsonResponse(result).setMessage("Index Created on " + keyspace+"."+tablename+"."+fieldName).toMap()).build();
619             } else {
620                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(result).setError("Unknown Error in create index.").toMap()).build();
621             }
622         } finally {
623             EELFLoggerDelegate.mdcRemove("keyspace");
624         }
625     }
626
627     /**
628      *
629      * @param insObj
630      * @param keyspace
631      * @param tablename
632      * @return
633      * @throws Exception
634      */
635     @POST
636     @Path("/{keyspace: .*}/tables/{tablename: .*}/rows")
637     @ApiOperation(value = "Insert Into Table", response = String.class,
638         notes = "Insert into table with data in json body.")
639     @Consumes(MediaType.APPLICATION_JSON)
640     @Produces(MediaType.APPLICATION_JSON)
641     @ApiResponses(value={
642         @ApiResponse(code=200, message = "Success",examples = @Example( value =  {
643             @ExampleProperty(mediaType="application/json",value = 
644                 "{\"message\" : \"Insert Successful\","
645                 + "\"status\" : \"SUCCESS\"}")
646         })),
647         @ApiResponse(code=400, message = "Failure - Generic",examples = @Example( value =  {
648             @ExampleProperty(mediaType="application/json",value = 
649                 "{\"error\" : \"<errorMessage>\","
650                 + "\"status\" : \"FAILURE\"}") 
651         }))
652     })  
653     public Response insertIntoTable(
654         @ApiParam(value = "Major Version",required = true) @PathParam("version") String version,
655         @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion,
656         @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
657         @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid,
658         @ApiParam(value = "Application namespace",required = false, hidden = true) @HeaderParam(NS) String ns,
659         @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
660         JsonInsert insObj,
661         @ApiParam(value = "Keyspace Name",
662             required = true) @PathParam("keyspace") String keyspace,
663         @ApiParam(value = "Table Name",
664             required = true) @PathParam("tablename") String tablename) {
665         try {
666             ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
667             if ( null == insObj ) {
668                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
669                         .setError(ResultType.BODYMISSING.getResult()).toMap()).build();
670             }
671             if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())){
672             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
673                 .setError("one or more path parameters are not set, please check and try again")
674                 .toMap()).build();
675             }
676             EELFLoggerDelegate.mdcPut("keyspace","(" + keyspace + ")");
677             PreparedQueryObject queryObject = new PreparedQueryObject();
678             TableMetadata tableInfo = null;
679             try {
680                 tableInfo = MusicDataStoreHandle.returnColumnMetadata(keyspace, tablename);
681                 if(tableInfo == null) {
682                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Table name doesn't exists. Please check the table name.").toMap()).build();
683                 }
684             } catch (MusicServiceException e) {
685                 logger.error(EELFLoggerDelegate.errorLogger, e, AppMessages.UNKNOWNERROR  ,ErrorSeverity.CRITICAL, ErrorTypes.GENERALSERVICEERROR);
686                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(e.getMessage()).toMap()).build();
687             }
688             String primaryKeyName = tableInfo.getPrimaryKey().get(0).getName();
689             StringBuilder fieldsString = new StringBuilder("(vector_ts,");
690             String vectorTs =
691                             String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis());
692             StringBuilder valueString = new StringBuilder("(" + "?" + ",");
693             queryObject.addValue(vectorTs);
694             
695             Map<String, Object> valuesMap = insObj.getValues();
696             if (valuesMap==null) {
697                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
698                         .setError("Nothing to insert. No values provided in request.").toMap()).build();
699             }
700             int counter = 0;
701             String primaryKey = "";
702             for (Map.Entry<String, Object> entry : valuesMap.entrySet()) {
703                 fieldsString.append("" + entry.getKey());
704                 Object valueObj = entry.getValue();
705                 if (primaryKeyName.equals(entry.getKey())) {
706                     primaryKey = entry.getValue() + "";
707                     primaryKey = primaryKey.replace("'", "''");
708                 }
709                 DataType colType = null;
710                 try {
711                     colType = tableInfo.getColumn(entry.getKey()).getType();
712                 } catch(NullPointerException ex) {
713                     logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage() +" Invalid column name : "+entry.getKey
714                         (), AppMessages.INCORRECTDATA  ,ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR, ex);
715                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Invalid column name : "+entry.getKey()).toMap()).build();
716                 }
717
718                 Object formattedValue = null;
719                 try {
720                     formattedValue = MusicUtil.convertToActualDataType(colType, valueObj);
721                 } catch (Exception e) {
722                     logger.error(EELFLoggerDelegate.errorLogger,e);
723                 }
724                 valueString.append("?");
725
726                 queryObject.addValue(formattedValue);
727
728                 if (counter == valuesMap.size() - 1) {
729                     fieldsString.append(")");
730                     valueString.append(")");
731                 } else {
732                     fieldsString.append(",");
733                     valueString.append(",");
734                 }
735                 counter = counter + 1;
736             }
737
738             //blobs..
739             Map<String, byte[]> objectMap = insObj.getObjectMap();
740             if(objectMap != null) {
741                 for (Map.Entry<String, byte[]> entry : objectMap.entrySet()) {
742                     if(counter > 0) {
743                         fieldsString.replace(fieldsString.length()-1, fieldsString.length(), ",");
744                         valueString.replace(valueString.length()-1, valueString.length(), ",");
745                     }
746                     fieldsString.append("" + entry.getKey());
747                     byte[] valueObj = entry.getValue();
748                     if (primaryKeyName.equals(entry.getKey())) {
749                         primaryKey = entry.getValue() + "";
750                         primaryKey = primaryKey.replace("'", "''");
751                     }
752                     DataType colType = tableInfo.getColumn(entry.getKey()).getType();
753                     ByteBuffer formattedValue = null;
754                     if(colType.toString().toLowerCase().contains("blob")) {
755                         formattedValue = MusicUtil.convertToActualDataType(colType, valueObj);
756                     }
757                     valueString.append("?");
758                     queryObject.addValue(formattedValue);
759                     counter = counter + 1;
760                     fieldsString.append(",");
761                     valueString.append(",");
762                 } 
763             }
764
765             if(primaryKey == null || primaryKey.length() <= 0) {
766                 logger.error(EELFLoggerDelegate.errorLogger, "Some required partition key parts are missing: "+primaryKeyName );
767                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.SYNTAXERROR).setError("Some required partition key parts are missing: "+primaryKeyName).toMap()).build();
768             }
769
770             fieldsString.replace(fieldsString.length()-1, fieldsString.length(), ")");
771             valueString.replace(valueString.length()-1, valueString.length(), ")");
772
773             queryObject.appendQueryString("INSERT INTO " + keyspace + "." + tablename + " "
774                             + fieldsString + " VALUES " + valueString);
775
776             String ttl = insObj.getTtl();
777             String timestamp = insObj.getTimestamp();
778
779             if ((ttl != null) && (timestamp != null)) {
780                 logger.info(EELFLoggerDelegate.applicationLogger, "both there");
781                 queryObject.appendQueryString(" USING TTL ? AND TIMESTAMP ?");
782                 queryObject.addValue(Integer.parseInt(ttl));
783                 queryObject.addValue(Long.parseLong(timestamp));
784             }
785
786             if ((ttl != null) && (timestamp == null)) {
787                 logger.info(EELFLoggerDelegate.applicationLogger, "ONLY TTL there");
788                 queryObject.appendQueryString(" USING TTL ?");
789                 queryObject.addValue(Integer.parseInt(ttl));
790             }
791
792             if ((ttl == null) && (timestamp != null)) {
793                 logger.info(EELFLoggerDelegate.applicationLogger, "ONLY timestamp there");
794                 queryObject.appendQueryString(" USING TIMESTAMP ?");
795                 queryObject.addValue(Long.parseLong(timestamp));
796             }
797
798             queryObject.appendQueryString(";");
799
800             ReturnType result = null;
801             String consistency = insObj.getConsistencyInfo().get("type");
802             if(consistency.equalsIgnoreCase(MusicUtil.EVENTUAL) && insObj.getConsistencyInfo().get("consistency") != null) {
803                 if(MusicUtil.isValidConsistency(insObj.getConsistencyInfo().get("consistency"))) {
804                     queryObject.setConsistency(insObj.getConsistencyInfo().get("consistency"));
805                 } else {
806                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.SYNTAXERROR).setError("Invalid Consistency type").toMap()).build();
807                 }
808             }
809             queryObject.setOperation("insert");
810             try {
811                 if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL)) {
812                     result = MusicCore.eventualPut(queryObject);
813                 } else if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) {
814                     String lockId = insObj.getConsistencyInfo().get("lockId");
815                     if(lockId == null) {
816                         logger.error(EELFLoggerDelegate.errorLogger,"LockId cannot be null. Create lock reference or"
817                                 + " use ATOMIC instead of CRITICAL", ErrorSeverity.FATAL, ErrorTypes.MUSICSERVICEERROR);
818                         return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("LockId cannot be null. Create lock "
819                                 + "and acquire lock or use ATOMIC instead of CRITICAL").toMap()).build();
820                     }
821                     result = MusicCore.criticalPut(keyspace, tablename, primaryKey, queryObject, lockId,null);
822                 } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) {
823                     result = MusicCore.atomicPut(keyspace, tablename, primaryKey, queryObject, null);
824                 }
825             } catch (Exception ex) {
826                 logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.UNKNOWNERROR  ,ErrorSeverity
827                     .WARN, ErrorTypes.MUSICSERVICEERROR, ex);
828                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
829             }
830             if (result==null) {
831                 logger.error(EELFLoggerDelegate.errorLogger,"Null result - Please Contact admin", AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes.MUSICSERVICEERROR);
832                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Null result - Please Contact admin").toMap()).build();
833             }else if(result.getResult() == ResultType.FAILURE) {
834                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(result.getResult()).setError(result.getMessage()).toMap()).build();
835             }
836             return response.status(Status.OK).entity(new JsonResponse(result.getResult()).setMessage("Insert Successful").toMap()).build();
837         } finally {
838             EELFLoggerDelegate.mdcRemove("keyspace");
839         }
840     }
841
842     /**
843      *
844      * @param insObj
845      * @param keyspace
846      * @param tablename
847      * @param info
848      * @return
849      * @throws MusicServiceException 
850      * @throws MusicQueryException 
851      * @throws Exception
852      */
853     @PUT
854     @Path("/{keyspace: .*}/tables/{tablename: .*}/rows")
855     @ApiOperation(value = "Update Table", response = String.class,
856         notes = "Update the table with the data in the JSON body.")
857     @Consumes(MediaType.APPLICATION_JSON)
858     @Produces(MediaType.APPLICATION_JSON)
859     public Response updateTable(
860         @ApiParam(value = "Major Version",
861             required = true) @PathParam("version") String version,
862         @ApiParam(value = "Minor Version",
863             required = false) @HeaderParam(XMINORVERSION) String minorVersion,
864         @ApiParam(value = "Patch Version",
865             required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
866         @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid,
867         @ApiParam(value = "Application namespace",
868             required = false, hidden = true) @HeaderParam(NS) String ns,
869         @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
870         JsonUpdate updateObj,
871         @ApiParam(value = "Keyspace Name",
872             required = true) @PathParam("keyspace") String keyspace,
873         @ApiParam(value = "Table Name",
874             required = true) @PathParam("tablename") String tablename,
875         @Context UriInfo info) throws MusicQueryException, MusicServiceException {
876         try {
877             ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
878             if ( null == updateObj ) {
879                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
880                         .setError(ResultType.BODYMISSING.getResult()).toMap()).build();
881             }
882             if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())){
883                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
884                     .setError("one or more path parameters are not set, please check and try again")
885                     .toMap()).build();
886             }
887             EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) ");
888             long startTime = System.currentTimeMillis();
889             String operationId = UUID.randomUUID().toString();  // just for infoging
890                                                                 // purposes.
891             String consistency = updateObj.getConsistencyInfo().get("type");
892
893             logger.info(EELFLoggerDelegate.applicationLogger, "--------------Music " + consistency
894                 + " update-" + operationId + "-------------------------");
895             // obtain the field value pairs of the update
896
897             PreparedQueryObject queryObject = new PreparedQueryObject();
898             Map<String, Object> valuesMap = updateObj.getValues();
899
900             TableMetadata tableInfo;
901             try {
902                 tableInfo = MusicDataStoreHandle.returnColumnMetadata(keyspace, tablename);
903             } catch (MusicServiceException e) {
904                 logger.error(EELFLoggerDelegate.errorLogger,e, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes
905                     .GENERALSERVICEERROR, e);
906                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(e.getMessage()).toMap()).build();
907             }
908             if (tableInfo == null) {
909                 logger.error(EELFLoggerDelegate.errorLogger,"Table information not found. Please check input for table name= "+tablename, AppMessages.MISSINGINFO  ,ErrorSeverity.WARN, ErrorTypes.AUTHENTICATIONERROR);
910                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
911                     .setError("Table information not found. Please check input for table name= "
912                     + keyspace + "." + tablename).toMap()).build();
913             }
914             String vectorTs = String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis());
915             StringBuilder fieldValueString = new StringBuilder("vector_ts=?,");
916             queryObject.addValue(vectorTs);
917             int counter = 0;
918             for (Map.Entry<String, Object> entry : valuesMap.entrySet()) {
919                 Object valueObj = entry.getValue();
920                 DataType colType = null;
921                 try {
922                     colType = tableInfo.getColumn(entry.getKey()).getType();
923                 } catch(NullPointerException ex) {
924                     logger.error(EELFLoggerDelegate.errorLogger, ex, "Invalid column name : "+entry.getKey(), ex);
925                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Invalid column name : "+entry.getKey()).toMap()).build();
926                 }
927                 Object valueString = null;
928                 try {
929                     valueString = MusicUtil.convertToActualDataType(colType, valueObj);
930                 } catch (Exception e) {
931                     logger.error(EELFLoggerDelegate.errorLogger,e);
932                 }
933                 fieldValueString.append(entry.getKey() + "= ?");
934                 queryObject.addValue(valueString);
935                 if (counter != valuesMap.size() - 1) {
936                     fieldValueString.append(",");
937                 }    
938                 counter = counter + 1;
939             }
940             String ttl = updateObj.getTtl();
941             String timestamp = updateObj.getTimestamp();
942
943             queryObject.appendQueryString("UPDATE " + keyspace + "." + tablename + " ");
944             if ((ttl != null) && (timestamp != null)) {
945                 logger.info("both there");
946                 queryObject.appendQueryString(" USING TTL ? AND TIMESTAMP ?");
947                 queryObject.addValue(Integer.parseInt(ttl));
948                 queryObject.addValue(Long.parseLong(timestamp));
949             }
950
951             if ((ttl != null) && (timestamp == null)) {
952                 logger.info("ONLY TTL there");
953                 queryObject.appendQueryString(" USING TTL ?");
954                 queryObject.addValue(Integer.parseInt(ttl));
955             }
956
957             if ((ttl == null) && (timestamp != null)) {
958                 logger.info("ONLY timestamp there");
959                 queryObject.appendQueryString(" USING TIMESTAMP ?");
960                 queryObject.addValue(Long.parseLong(timestamp));
961             }
962             // get the row specifier
963             RowIdentifier rowId = null;
964             try {
965                 rowId = getRowIdentifier(keyspace, tablename, info.getQueryParameters(), queryObject);
966                 if(rowId == null || rowId.primarKeyValue.isEmpty()) {
967                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
968                             .setError("Mandatory WHERE clause is missing. Please check the input request.").toMap()).build();
969                 }
970             } catch (MusicServiceException ex) {
971                 logger.error(EELFLoggerDelegate.errorLogger,ex, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes
972                     .GENERALSERVICEERROR, ex);
973                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
974             }
975
976             queryObject.appendQueryString(
977                 " SET " + fieldValueString + " WHERE " + rowId.rowIdString + ";");
978
979             // get the conditional, if any
980             Condition conditionInfo;
981             if (updateObj.getConditions() == null) {
982                 conditionInfo = null;
983             } else {
984                 // to avoid parsing repeatedly, just send the select query to obtain row
985                 PreparedQueryObject selectQuery = new PreparedQueryObject();
986                 selectQuery.appendQueryString("SELECT *  FROM " + keyspace + "." + tablename + " WHERE "
987                     + rowId.rowIdString + ";");
988                 selectQuery.addValue(rowId.primarKeyValue);
989                 conditionInfo = new Condition(updateObj.getConditions(), selectQuery);
990             }
991
992             ReturnType operationResult = null;
993             long jsonParseCompletionTime = System.currentTimeMillis();
994
995             if(consistency.equalsIgnoreCase(MusicUtil.EVENTUAL) && updateObj.getConsistencyInfo().get("consistency") != null) {
996                 if(MusicUtil.isValidConsistency(updateObj.getConsistencyInfo().get("consistency"))) {
997                     queryObject.setConsistency(updateObj.getConsistencyInfo().get("consistency"));
998                 } else {
999                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.SYNTAXERROR).setError("Invalid Consistency type").toMap()).build();
1000                 }
1001             }
1002             queryObject.setOperation("update");
1003             if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL)) {
1004                 operationResult = MusicCore.eventualPut(queryObject);
1005             } else if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) {
1006                 String lockId = updateObj.getConsistencyInfo().get("lockId");
1007                 if(lockId == null) {
1008                     logger.error(EELFLoggerDelegate.errorLogger,"LockId cannot be null. Create lock reference or"
1009                             + " use ATOMIC instead of CRITICAL", ErrorSeverity.FATAL, ErrorTypes.MUSICSERVICEERROR);
1010                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("LockId cannot be null. Create lock "
1011                             + "and acquire lock or use ATOMIC instead of CRITICAL").toMap()).build();
1012                 }
1013                 try {
1014                     operationResult = MusicCore.criticalPut(keyspace, tablename, rowId.primarKeyValue,
1015                         queryObject, lockId, conditionInfo);
1016                 } catch ( Exception e) {
1017                     logger.error(EELFLoggerDelegate.errorLogger,e, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN,
1018                         ErrorTypes.GENERALSERVICEERROR, e);
1019                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Error doing critical put: " + e.getMessage()).toMap()).build();
1020                 }
1021             } else if (consistency.equalsIgnoreCase("atomic_delete_lock")) {
1022                 // this function is mainly for the benchmarks
1023                 try {
1024                     operationResult = MusicCore.atomicPutWithDeleteLock(keyspace, tablename,
1025                         rowId.primarKeyValue, queryObject, conditionInfo);
1026                 } catch (MusicLockingException e) {
1027                     logger.error(EELFLoggerDelegate.errorLogger,e, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN,
1028                         ErrorTypes.GENERALSERVICEERROR, e);
1029                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(e.getMessage()).toMap()).build();
1030                 }
1031             } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) {
1032                 try {
1033                     operationResult = MusicCore.atomicPut(keyspace, tablename, rowId.primarKeyValue,
1034                         queryObject, conditionInfo);
1035                 } catch (MusicLockingException e) {
1036                     logger.error(EELFLoggerDelegate.errorLogger,e, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR, e);
1037                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(e.getMessage()).toMap()).build();
1038                 }
1039             } else if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL_NB)) {
1040                 operationResult = MusicCore.eventualPut_nb(queryObject, keyspace, tablename, rowId.primarKeyValue);
1041             }
1042             long actualUpdateCompletionTime = System.currentTimeMillis();
1043
1044             long endTime = System.currentTimeMillis();
1045             String timingString = "Time taken in ms for Music " + consistency + " update-" + operationId
1046                 + ":" + "|total operation time:" + (endTime - startTime)
1047                 + "|json parsing time:" + (jsonParseCompletionTime - startTime)
1048                 + "|update time:" + (actualUpdateCompletionTime - jsonParseCompletionTime)
1049                 + "|";
1050
1051             if (operationResult != null && operationResult.getTimingInfo() != null) {
1052                 String lockManagementTime = operationResult.getTimingInfo();
1053                 timingString = timingString + lockManagementTime;
1054             }
1055             logger.info(EELFLoggerDelegate.applicationLogger, timingString);
1056
1057             if (operationResult==null) {
1058                 logger.error(EELFLoggerDelegate.errorLogger,"Null result - Please Contact admin", AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR);
1059                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Null result - Please Contact admin").toMap()).build();
1060             }
1061             if ( operationResult.getResult() == ResultType.SUCCESS ) {
1062                 return response.status(Status.OK).entity(new JsonResponse(operationResult.getResult()).setMessage(operationResult.getMessage()).toMap()).build();
1063             } else {
1064                 logger.error(EELFLoggerDelegate.errorLogger,operationResult.getMessage(), AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR);
1065                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(operationResult.getResult()).setError(operationResult.getMessage()).toMap()).build();
1066             }
1067         } finally {
1068             EELFLoggerDelegate.mdcRemove("keyspace");
1069         }
1070     }
1071
1072     /**
1073      *
1074      * @param delObj
1075      * @param keyspace
1076      * @param tablename
1077      * @param info
1078      * @return
1079      * @throws MusicServiceException 
1080      * @throws MusicQueryException 
1081      * @throws Exception
1082      */
1083     @DELETE
1084     @Path("/{keyspace: .*}/tables/{tablename: .*}/rows")
1085     @ApiOperation(value = "Delete From table", response = String.class,
1086         notes = "Delete from a table, the row or parts of a row. Based on JSON body.")
1087     @Consumes(MediaType.APPLICATION_JSON)
1088     @Produces(MediaType.APPLICATION_JSON)
1089     public Response deleteFromTable(
1090         @ApiParam(value = "Major Version",
1091             required = true) @PathParam("version") String version,
1092         @ApiParam(value = "Minor Version",
1093             required = false) @HeaderParam(XMINORVERSION) String minorVersion,
1094         @ApiParam(value = "Patch Version",
1095             required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
1096         @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid,
1097         @ApiParam(value = "Application namespace",
1098             required = false, hidden = true) @HeaderParam(NS) String ns,
1099         @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
1100         JsonDelete delObj,
1101         @ApiParam(value = "Keyspace Name",
1102             required = true) @PathParam("keyspace") String keyspace,
1103         @ApiParam(value = "Table Name",
1104             required = true) @PathParam("tablename") String tablename,
1105         @Context UriInfo info) throws MusicQueryException, MusicServiceException {
1106         try {
1107             ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
1108             if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())){
1109                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
1110                     .setError("one or more path parameters are not set, please check and try again")
1111                     .toMap()).build();
1112             }
1113             EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) ");
1114             if(delObj == null) {
1115                 logger.error(EELFLoggerDelegate.errorLogger,ResultType.BODYMISSING.getResult(), AppMessages.MISSINGDATA  ,ErrorSeverity.WARN, ErrorTypes.DATAERROR);
1116                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ResultType.BODYMISSING.getResult()).toMap()).build();
1117             }
1118             PreparedQueryObject queryObject = new PreparedQueryObject();
1119             StringBuilder columnString = new StringBuilder();
1120
1121             int counter = 0;
1122             List<String> columnList = delObj.getColumns();
1123             if (columnList != null) {
1124                 for (String column : columnList) {
1125                     columnString.append(column);
1126                     if (counter != columnList.size() - 1)
1127                         columnString.append(",");
1128                     counter = counter + 1;
1129                 }
1130             }
1131
1132             // get the row specifier
1133             RowIdentifier rowId = null;
1134             try {
1135                 rowId = getRowIdentifier(keyspace, tablename, info.getQueryParameters(), queryObject);
1136             } catch (MusicServiceException ex) {
1137                 logger.error(EELFLoggerDelegate.errorLogger,ex, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes
1138                     .GENERALSERVICEERROR, ex);
1139                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
1140             }
1141             String rowSpec = rowId.rowIdString.toString();
1142
1143             if ((columnList != null) && (!rowSpec.isEmpty())) {
1144                 queryObject.appendQueryString("DELETE " + columnString + " FROM " + keyspace + "."
1145                                 + tablename + " WHERE " + rowSpec + ";");
1146             }
1147
1148             if ((columnList == null) && (!rowSpec.isEmpty())) {
1149                 queryObject.appendQueryString("DELETE FROM " + keyspace + "." + tablename + " WHERE "
1150                                 + rowSpec + ";");
1151             }
1152
1153             if ((columnList != null) && (rowSpec.isEmpty())) {
1154                 queryObject.appendQueryString(
1155                                 "DELETE " + columnString + " FROM " + keyspace + "." + rowSpec + ";");
1156             }
1157             // get the conditional, if any
1158             Condition conditionInfo;
1159             if (delObj.getConditions() == null) {
1160                 conditionInfo = null;
1161             } else {
1162                 // to avoid parsing repeatedly, just send the select query to
1163                 // obtain row
1164                 PreparedQueryObject selectQuery = new PreparedQueryObject();
1165                 selectQuery.appendQueryString("SELECT *  FROM " + keyspace + "." + tablename + " WHERE "
1166                     + rowId.rowIdString + ";");
1167                 selectQuery.addValue(rowId.primarKeyValue);
1168                 conditionInfo = new Condition(delObj.getConditions(), selectQuery);
1169             }
1170
1171             String consistency = delObj.getConsistencyInfo().get("type");
1172
1173
1174             if(consistency.equalsIgnoreCase(MusicUtil.EVENTUAL) && delObj.getConsistencyInfo().get("consistency")!=null) {
1175                 if(MusicUtil.isValidConsistency(delObj.getConsistencyInfo().get("consistency"))) {
1176                     queryObject.setConsistency(delObj.getConsistencyInfo().get("consistency"));
1177                 } else {
1178                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.SYNTAXERROR)
1179                         .setError("Invalid Consistency type").toMap()).build();
1180                 }
1181             }
1182             ReturnType operationResult = null;
1183             queryObject.setOperation("delete");
1184             try {
1185                 if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL))
1186                     operationResult = MusicCore.eventualPut(queryObject);
1187                 else if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) {
1188                     String lockId = delObj.getConsistencyInfo().get("lockId");
1189                     if(lockId == null) {
1190                         logger.error(EELFLoggerDelegate.errorLogger,"LockId cannot be null. Create lock reference or"
1191                             + " use ATOMIC instead of CRITICAL", ErrorSeverity.FATAL, ErrorTypes.MUSICSERVICEERROR);
1192                         return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("LockId cannot be null. Create lock "
1193                             + "and acquire lock or use ATOMIC instead of CRITICAL").toMap()).build();
1194                     }
1195                     operationResult = MusicCore.criticalPut(keyspace, tablename, rowId.primarKeyValue,
1196                         queryObject, lockId, conditionInfo);
1197                 } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) {
1198                     operationResult = MusicCore.atomicPut(keyspace, tablename, rowId.primarKeyValue,
1199                         queryObject, conditionInfo);
1200                 } else if(consistency.equalsIgnoreCase(MusicUtil.EVENTUAL_NB)) {                    
1201                     operationResult = MusicCore.eventualPut_nb(queryObject, keyspace, tablename, rowId.primarKeyValue);
1202                 }
1203             } catch (MusicLockingException e) {
1204                 logger.error(EELFLoggerDelegate.errorLogger,e, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR, e);
1205                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
1206                         .setError("Unable to perform Delete operation. Exception from music").toMap()).build();
1207             }
1208             if (operationResult==null) {
1209                 logger.error(EELFLoggerDelegate.errorLogger,"Null result - Please Contact admin", AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR);
1210                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Null result - Please Contact admin").toMap()).build();
1211             }
1212             if (operationResult.getResult().equals(ResultType.SUCCESS)) {
1213                 return response.status(Status.OK).entity(new JsonResponse(operationResult.getResult()).setMessage(operationResult.getMessage()).toMap()).build();
1214             } else {
1215                 logger.error(EELFLoggerDelegate.errorLogger,operationResult.getMessage(), AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR);
1216                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(operationResult.getMessage()).toMap()).build();
1217             }
1218         } finally {
1219             EELFLoggerDelegate.mdcRemove("keyspace");
1220         }
1221     }
1222
1223     /**
1224      *
1225      * @param tabObj
1226      * @param keyspace
1227      * @param tablename
1228      * @throws Exception
1229      */
1230     @DELETE
1231     @Path("/{keyspace: .*}/tables/{tablename: .*}")
1232     @ApiOperation(value = "Drop Table", response = String.class)
1233     @Produces(MediaType.APPLICATION_JSON)
1234     @ApiResponses(value={
1235         @ApiResponse(code=200, message = "Success",examples = @Example( value =  {
1236             @ExampleProperty(mediaType="application/json",value = 
1237                 "{\"status\" : \"SUCCESS\"}")
1238         })),
1239         @ApiResponse(code=400, message = "Failure",examples = @Example( value =  {
1240             @ExampleProperty(mediaType="application/json",value = 
1241                 "{\"error\" : \"<errorMessage>\","
1242                 + "\"status\" : \"FAILURE\"}") 
1243         }))
1244     })
1245     public Response dropTable(
1246         @ApiParam(value = "Major Version",
1247             required = true) @PathParam("version") String version,
1248         @ApiParam(value = "Minor Version",
1249             required = false) @HeaderParam(XMINORVERSION) String minorVersion,
1250         @ApiParam(value = "Patch Version",
1251             required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
1252         @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid,
1253         @ApiParam(value = "Application namespace",
1254             required = false, hidden = true) @HeaderParam(NS) String ns,
1255         @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
1256         @ApiParam(value = "Keyspace Name",
1257             required = true) @PathParam("keyspace") String keyspace,
1258         @ApiParam(value = "Table Name",
1259             required = true) @PathParam("tablename") String tablename) throws Exception {
1260         try {
1261             ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
1262             if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())){
1263                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
1264                     .setError("one or more path parameters are not set, please check and try again")
1265                     .toMap()).build();
1266             }
1267             EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) ");
1268             String consistency = "eventual";// for now this needs only eventual consistency
1269             PreparedQueryObject query = new PreparedQueryObject();
1270             query.appendQueryString("DROP TABLE  " + keyspace + "." + tablename + ";");
1271             try {
1272                 return response.status(Status.OK).entity(new JsonResponse(MusicCore.nonKeyRelatedPut(query, consistency)).toMap()).build();
1273             } catch (MusicQueryException ex) {
1274                 logger.error(EELFLoggerDelegate.errorLogger, ex.getMessage(), AppMessages.QUERYERROR,ErrorSeverity.WARN
1275                     , ErrorTypes.QUERYERROR);
1276                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
1277             } catch (MusicServiceException ex) {
1278                 logger.error(EELFLoggerDelegate.errorLogger, ex.getMessage(), AppMessages.MISSINGINFO  ,ErrorSeverity.WARN
1279                 , ErrorTypes.GENERALSERVICEERROR,ex);
1280                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
1281             }
1282         } finally {
1283             EELFLoggerDelegate.mdcRemove("keyspace");
1284         }
1285     }
1286
1287     /**
1288      *
1289      * @param selObj
1290      * @param keyspace
1291      * @param tablename
1292      * @param info
1293      * @return
1294      */
1295     @PUT
1296     @Path("/{keyspace: .*}/tables/{tablename: .*}/rows/criticalget")
1297     @ApiOperation(value = "** Depreciated ** - Select Critical", response = Map.class,
1298         notes = "This API is depreciated in favor of the regular select api.\n"
1299         + "Avaliable to use with the select api by providing a minorVersion of 1 "
1300         + "and patchVersion of 0.\n"
1301         + "Critical Get requires parameter rowId=value and consistency in order to work.\n"
1302         + "It will fail if either are missing.")
1303     @Consumes(MediaType.APPLICATION_JSON)
1304     @Produces(MediaType.APPLICATION_JSON)
1305     @ApiResponses(value={
1306         @ApiResponse(code=200, message = "Success",examples = @Example( value =  {
1307             @ExampleProperty(mediaType="application/json",value = 
1308                 "{\"result\":{\"row 0\":{\"address\":"
1309                 + "{\"city\":\"Someplace\",\"street\":\"1 Some way\"},"
1310                 + "\"emp_salary\":50,\"emp_name\":\"tom\",\"emp_id\":"
1311                 + "\"cfd66ccc-d857-4e90-b1e5-df98a3d40cd6\"}},\"status\":\"SUCCESS\"}")
1312         })),
1313         @ApiResponse(code=400, message = "Failure",examples = @Example( value =  {
1314             @ExampleProperty(mediaType="application/json",value = 
1315                 "{\"error\" : \"<errorMessage>\","
1316                 + "\"status\" : \"FAILURE\"}") 
1317         }))
1318     })
1319     public Response selectCritical(
1320         @ApiParam(value = "Major Version",
1321     required = true) @PathParam("version") String version,
1322         @ApiParam(value = "Minor Version",example = "0",
1323             required = false) @HeaderParam(XMINORVERSION) String minorVersion,
1324         @ApiParam(value = "Patch Version",example = "0",
1325             required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
1326         @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid,
1327         @ApiParam(value = "Application namespace",
1328             required = false, hidden = true) @HeaderParam(NS) String ns,
1329         @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
1330         JsonInsert selObj,
1331         @ApiParam(value = "Keyspace Name",
1332             required = true) @PathParam("keyspace") String keyspace,
1333         @ApiParam(value = "Table Name",
1334             required = true) @PathParam("tablename") String tablename,
1335         @Context UriInfo info) throws Exception {
1336         try {
1337             ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
1338             if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())) { 
1339                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
1340                     .setError("one or more path parameters are not set, please check and try again")
1341                     .toMap()).build();
1342             }
1343             EELFLoggerDelegate.mdcPut("keyspace", "( " + keyspace + " )");
1344             if (info.getQueryParameters().isEmpty()) {
1345                 logger.error(EELFLoggerDelegate.errorLogger,RestMusicDataAPI.PARAMETER_ERROR, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes
1346                 .GENERALSERVICEERROR);
1347                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(RestMusicDataAPI.PARAMETER_ERROR).toMap()).build();
1348             }
1349             if (selObj == null || selObj.getConsistencyInfo().isEmpty()) {
1350                 String error = " Missing Body or Consistency type.";
1351                 logger.error(EELFLoggerDelegate.errorLogger,ResultType.BODYMISSING.getResult() + error, AppMessages.MISSINGDATA  ,ErrorSeverity.WARN, ErrorTypes.DATAERROR);
1352                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ResultType.BODYMISSING.getResult() + error).toMap()).build();
1353             }
1354
1355             String lockId = selObj.getConsistencyInfo().get("lockId");
1356             PreparedQueryObject queryObject = new PreparedQueryObject();
1357             RowIdentifier rowId = null;
1358             try {
1359                 rowId = getRowIdentifier(keyspace, tablename, info.getQueryParameters(), queryObject);
1360                 if ( "".equals(rowId)) {
1361                     logger.error(EELFLoggerDelegate.errorLogger,RestMusicDataAPI.PARAMETER_ERROR, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes
1362                     .GENERALSERVICEERROR);
1363                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(RestMusicDataAPI.PARAMETER_ERROR).toMap()).build();
1364                 }
1365             } catch (MusicServiceException ex) {
1366                 logger.error(EELFLoggerDelegate.errorLogger,ex, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes
1367                     .GENERALSERVICEERROR, ex);
1368                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
1369             }
1370             queryObject.appendQueryString(
1371                 "SELECT *  FROM " + keyspace + "." + tablename + " WHERE " + rowId.rowIdString + ";");
1372
1373             ResultSet results = null;
1374
1375             String consistency = selObj.getConsistencyInfo().get("type");
1376             try {
1377                 if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) {
1378                     if(lockId == null) {
1379                         logger.error(EELFLoggerDelegate.errorLogger,"LockId cannot be null. Create lock reference or"
1380                             + " use ATOMIC instead of CRITICAL", ErrorSeverity.FATAL, ErrorTypes.MUSICSERVICEERROR);
1381                         return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("LockId cannot be null. Create lock "
1382                             + "and acquire lock or use ATOMIC instead of CRITICAL").toMap()).build();
1383                     }
1384                     results = MusicCore.criticalGet(keyspace, tablename, rowId.primarKeyValue, queryObject,lockId);
1385                 } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) {
1386                     results = MusicCore.atomicGet(keyspace, tablename, rowId.primarKeyValue, queryObject);
1387                 } else {
1388                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
1389                         .setError("Consistency must be: " + MusicUtil.ATOMIC + " or " + MusicUtil.CRITICAL)
1390                         .toMap()).build();
1391                 }
1392             } catch ( MusicLockingException | MusicServiceException me ) {
1393                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Music Exception" + me.getMessage()).toMap()).build();
1394             } catch ( Exception ex) {
1395                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
1396             }
1397             
1398             if(results!=null && results.getAvailableWithoutFetching() >0) {
1399                 return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setDataResult(MusicDataStoreHandle.marshallResults(results)).toMap()).build();
1400             }
1401             return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setError("No data found").toMap()).build();
1402         } finally {
1403             EELFLoggerDelegate.mdcRemove("keyspace");
1404         }
1405     }
1406
1407     /**
1408      * This API will replace the original select and provide a single API fro select and critical. 
1409      * The idea is to depreciate the older api of criticalGet and use a single API. 
1410      * 
1411      * @param selObj
1412      * @param keyspace
1413      * @param tablename
1414      * @param info
1415      * @return
1416      */
1417     @GET
1418     @Path("/{keyspace: .*}/tables/{tablename: .*}/rows")
1419     @ApiOperation(value = "Select", response = Map.class,
1420         notes = "This has 2 versions: if minorVersion and patchVersion is null or 0, this will be a Eventual Select only.\n"
1421         + "If minorVersion is 1 and patchVersion is 0, this will act like the Critical Select \n"
1422         + "Critical Get requires parameter rowId=value and consistency in order to work.\n"
1423         + "If parameters are missing or consistency information is missing. An eventual select will be preformed.")
1424     @Consumes(MediaType.APPLICATION_JSON)
1425     @Produces(MediaType.APPLICATION_JSON)
1426     @ApiResponses(value={
1427         @ApiResponse(code=200, message = "Success",examples = @Example( value =  {
1428             @ExampleProperty(mediaType="application/json",value = 
1429                 "{\"result\":{\"row 0\":{\"address\":"
1430                 + "{\"city\":\"Someplace\",\"street\":\"1 Some way\"},"
1431                 + "\"emp_salary\":50,\"emp_name\":\"tom\",\"emp_id\":"
1432                 + "\"cfd66ccc-d857-4e90-b1e5-df98a3d40cd6\"}},\"status\":\"SUCCESS\"}")
1433         })),
1434         @ApiResponse(code=400, message = "Failure",examples = @Example( value =  {
1435             @ExampleProperty(mediaType="application/json",value = 
1436                 "{\"error\" : \"<errorMessage>\","
1437                 + "\"status\" : \"FAILURE\"}") 
1438         }))
1439     })
1440     public Response selectWithCritical(
1441         @ApiParam(value = "Major Version",example = "v2", 
1442             required = true) @PathParam("version") String version,
1443         @ApiParam(value = "Minor Version",example = "1",
1444             required = false) @HeaderParam(XMINORVERSION) String minorVersion,
1445         @ApiParam(value = "Patch Version",example = "0",
1446             required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
1447         @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid,
1448         @ApiParam(value = "Application namespace",
1449             required = false,hidden = true) @HeaderParam(NS) String ns,
1450         @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
1451         JsonInsert selObj,
1452         @ApiParam(value = "Keyspace Name", required = true) @PathParam("keyspace") String keyspace,
1453         @ApiParam(value = "Table Name", required = true) @PathParam("tablename") String tablename,
1454         @Context UriInfo info) throws Exception {
1455         if ((minorVersion != null && patchVersion != null) &&
1456             (Integer.parseInt(minorVersion) == 1 && Integer.parseInt(patchVersion) == 0) &&
1457             (!(null == selObj) && !selObj.getConsistencyInfo().isEmpty())) {
1458             return selectCritical(version, minorVersion, patchVersion, aid, ns, authorization, selObj, keyspace, tablename, info);
1459         } else {
1460             return select(version, minorVersion, patchVersion, aid, ns, authorization, keyspace, tablename, info);
1461         }
1462     }
1463
1464     /**
1465      *
1466      * @param keyspace
1467      * @param tablename
1468      * @param info
1469      * @return
1470      * @throws Exception
1471      */
1472     private Response select(
1473         String version,String minorVersion,String patchVersion,
1474         String aid,String ns,String authorization,String keyspace,        
1475         String tablename,UriInfo info) throws Exception {
1476         try { 
1477             ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
1478             if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())){
1479                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
1480                     .setError("one or more path parameters are not set, please check and try again")
1481                     .toMap()).build();
1482             }
1483             EELFLoggerDelegate.mdcPut("keyspace", "( " + keyspace + " ) ");
1484             PreparedQueryObject queryObject = new PreparedQueryObject();
1485
1486             if (info.getQueryParameters().isEmpty()) { // select all
1487                 queryObject.appendQueryString("SELECT *  FROM " + keyspace + "." + tablename + ";");
1488             } else {
1489                 int limit = -1; // do not limit the number of results
1490                 try {
1491                     queryObject = selectSpecificQuery(keyspace, tablename, info, limit);
1492                 } catch (MusicServiceException ex) {
1493                     logger.error(EELFLoggerDelegate.errorLogger, ex, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN,
1494                         ErrorTypes.GENERALSERVICEERROR, ex);
1495                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
1496                 }
1497             }
1498             try {
1499                 ResultSet results = MusicCore.get(queryObject);
1500                 if(results.getAvailableWithoutFetching() >0) {
1501                     return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setDataResult(MusicDataStoreHandle.marshallResults(results)).toMap()).build();
1502                 }
1503                 return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setDataResult(MusicDataStoreHandle.marshallResults(results)).setError("No data found").toMap()).build();
1504             } catch (MusicServiceException ex) {
1505                 logger.error(EELFLoggerDelegate.errorLogger, ex, AppMessages.UNKNOWNERROR  ,ErrorSeverity.ERROR,
1506                     ErrorTypes.MUSICSERVICEERROR, ex);
1507                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
1508             }
1509         } finally {
1510             EELFLoggerDelegate.mdcRemove("keyspace");
1511         }
1512     }
1513
1514     /**
1515      *
1516      * @param keyspace
1517      * @param tablename
1518      * @param info
1519      * @param limit
1520      * @return
1521      * @throws MusicServiceException
1522      */
1523     public PreparedQueryObject selectSpecificQuery(String keyspace,
1524         String tablename, UriInfo info, int limit)
1525         throws MusicServiceException {
1526         PreparedQueryObject queryObject = new PreparedQueryObject();
1527         StringBuilder rowIdString = getRowIdentifier(keyspace, 
1528             tablename,info.getQueryParameters(),queryObject).rowIdString;
1529         queryObject.appendQueryString(
1530             "SELECT *  FROM " + keyspace + "." + tablename + " WHERE " + rowIdString);
1531         if (limit != -1) {
1532             queryObject.appendQueryString(" LIMIT " + limit);
1533         }
1534         queryObject.appendQueryString(";");
1535         return queryObject;
1536     }
1537
1538     /**
1539      *
1540      * @param keyspace
1541      * @param tablename
1542      * @param rowParams
1543      * @param queryObject
1544      * @return
1545      * @throws MusicServiceException
1546      */
1547     private RowIdentifier getRowIdentifier(String keyspace, String tablename,
1548         MultivaluedMap<String, String> rowParams, PreparedQueryObject queryObject)
1549         throws MusicServiceException {
1550         StringBuilder rowSpec = new StringBuilder();
1551         int counter = 0;
1552         TableMetadata tableInfo = MusicDataStoreHandle.returnColumnMetadata(keyspace, tablename);
1553         if (tableInfo == null) {
1554             logger.error(EELFLoggerDelegate.errorLogger,
1555                 "Table information not found. Please check input for table name= "
1556                 + keyspace + "." + tablename);
1557             throw new MusicServiceException(
1558                 "Table information not found. Please check input for table name= "
1559                 + keyspace + "." + tablename);
1560         }
1561         StringBuilder primaryKey = new StringBuilder();
1562         for (MultivaluedMap.Entry<String, List<String>> entry : rowParams.entrySet()) {
1563             String keyName = entry.getKey();
1564             List<String> valueList = entry.getValue();
1565             String indValue = valueList.get(0);
1566             DataType colType = null;
1567             Object formattedValue = null;
1568             try {
1569                 colType = tableInfo.getColumn(entry.getKey()).getType();
1570                 formattedValue = MusicUtil.convertToActualDataType(colType, indValue);
1571             } catch (Exception e) {
1572                 logger.error(EELFLoggerDelegate.errorLogger,e);
1573             }
1574             if(tableInfo.getPrimaryKey().get(0).getName().equals(entry.getKey())) {
1575                 primaryKey.append(indValue);
1576             }
1577             rowSpec.append(keyName + "= ?");
1578             queryObject.addValue(formattedValue);
1579             if (counter != rowParams.size() - 1) {
1580                 rowSpec.append(" AND ");
1581             }
1582             counter = counter + 1;
1583         }
1584         return new RowIdentifier(primaryKey.toString(), rowSpec, queryObject);
1585     }
1586 }