2 * ============LICENSE_START========================================== org.onap.music
3 * =================================================================== Copyright (c) 2017 AT&T
4 * Intellectual Property ===================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
6 * in compliance with the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software distributed under the License
11 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12 * or implied. See the License for the specific language governing permissions and limitations under
15 * ============LICENSE_END=============================================
16 * ====================================================================
18 package org.onap.music.rest;
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.List;
24 import java.util.UUID;
25 import javax.servlet.http.HttpServletResponse;
26 import javax.ws.rs.Consumes;
27 import javax.ws.rs.DELETE;
28 import javax.ws.rs.GET;
29 import javax.ws.rs.HeaderParam;
30 import javax.ws.rs.POST;
31 import javax.ws.rs.PUT;
32 import javax.ws.rs.Path;
33 import javax.ws.rs.PathParam;
34 import javax.ws.rs.Produces;
35 import javax.ws.rs.core.Context;
36 import javax.ws.rs.core.MediaType;
37 import javax.ws.rs.core.MultivaluedMap;
38 import javax.ws.rs.core.UriInfo;
39 import org.onap.music.datastore.PreparedQueryObject;
40 import org.onap.music.datastore.jsonobjects.JsonDelete;
41 import org.onap.music.datastore.jsonobjects.JsonInsert;
42 import org.onap.music.datastore.jsonobjects.JsonKeySpace;
43 import org.onap.music.datastore.jsonobjects.JsonTable;
44 import org.onap.music.datastore.jsonobjects.JsonUpdate;
45 import org.onap.music.eelf.logging.EELFLoggerDelegate;
46 import org.onap.music.exceptions.MusicLockingException;
47 import org.onap.music.exceptions.MusicServiceException;
48 import org.onap.music.main.CachingUtil;
49 import org.onap.music.main.MusicCore;
50 import org.onap.music.main.MusicCore.Condition;
51 import org.onap.music.main.MusicUtil;
52 import org.onap.music.main.ResultType;
53 import org.onap.music.main.ReturnType;
54 import org.onap.music.response.jsonobjects.JsonResponse;
56 import com.att.eelf.configuration.EELFLogger;
57 import com.datastax.driver.core.DataType;
58 import com.datastax.driver.core.ResultSet;
59 import com.datastax.driver.core.Row;
60 import com.datastax.driver.core.TableMetadata;
61 import io.swagger.annotations.Api;
62 import io.swagger.annotations.ApiOperation;
63 import io.swagger.annotations.ApiParam;
65 @Path("/v{version: [0-9]+}/keyspaces")
66 @Api(value = "Data Api")
67 public class RestMusicDataAPI {
69 * Header values for Versioning X-minorVersion *** - Used to request or communicate a MINOR
70 * version back from the client to the server, and from the server back to the client - This
71 * will be the MINOR version requested by the client, or the MINOR version of the last MAJOR
72 * version (if not specified by the client on the request) - Contains a single position value
73 * (e.g. if the full version is 1.24.5, X-minorVersion = "24") - Is optional for the client on
74 * request; however, this header should be provided if the client needs to take advantage of
75 * MINOR incremented version functionality - Is mandatory for the server on response
77 *** X-patchVersion *** - Used only to communicate a PATCH version in a response for
78 * troubleshooting purposes only, and will not be provided by the client on request - This will
79 * be the latest PATCH version of the MINOR requested by the client, or the latest PATCH version
80 * of the MAJOR (if not specified by the client on the request) - Contains a single position
81 * value (e.g. if the full version is 1.24.5, X-patchVersion = "5") - Is mandatory for the
84 *** X-latestVersion *** - Used only to communicate an API's latest version - Is mandatory for the
85 * server on response, and shall include the entire version of the API (e.g. if the full version
86 * is 1.24.5, X-latestVersion = "1.24.5") - Used in the response to inform clients that they are
87 * not using the latest version of the API
91 private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(RestMusicDataAPI.class);
92 private static String xLatestVersion = "X-latestVersion";
94 private class RowIdentifier {
95 public String primarKeyValue;
96 public StringBuilder rowIdString;
97 @SuppressWarnings("unused")
98 public PreparedQueryObject queryObject;// the string with all the row
99 // identifiers separated by AND
101 public RowIdentifier(String primaryKeyValue, StringBuilder rowIdString,
102 PreparedQueryObject queryObject) {
103 this.primarKeyValue = primaryKeyValue;
104 this.rowIdString = rowIdString;
105 this.queryObject = queryObject;
109 @SuppressWarnings("unused")
110 private String buildVersion(String major, String minor, String patch) {
112 major += "." + minor;
114 major += "." + patch;
121 * Create Keyspace REST
124 * @param keyspaceName
130 @ApiOperation(value = "Create Keyspace", response = String.class)
131 @Consumes(MediaType.APPLICATION_JSON)
132 @Produces(MediaType.APPLICATION_JSON)
133 public Map<String, Object> createKeySpace(
134 @ApiParam(value = "Major Version",
135 required = true) @PathParam("version") String version,
136 @ApiParam(value = "Minor Version",
137 required = false) @HeaderParam("X-minorVersion") String minorVersion,
138 @ApiParam(value = "Patch Version",
139 required = false) @HeaderParam("X-patchVersion") String patchVersion,
140 @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid,
141 @ApiParam(value = "Application namespace",
142 required = true) @HeaderParam("ns") String ns,
143 @ApiParam(value = "userId",
144 required = true) @HeaderParam("userId") String userId,
145 @ApiParam(value = "Password",
146 required = true) @HeaderParam("password") String password,
147 JsonKeySpace kspObject,
148 @ApiParam(value = "Keyspace Name",
149 required = true) @PathParam("name") String keyspaceName,
150 @Context HttpServletResponse response) {
151 Map<String, Object> resultMap = CachingUtil.verifyOnboarding(ns, userId, password);
152 response.addHeader(xLatestVersion, MusicUtil.getVersion());
153 if (!resultMap.isEmpty()) {
158 resultMap = MusicCore.autheticateUser(ns, userId, password, keyspaceName, aid,
160 } catch (Exception e) {
161 logger.error(EELFLoggerDelegate.applicationLogger,
162 "Exception while authenting the user.");
165 String newAid = null;
166 if (!resultMap.isEmpty()) {
167 if (resultMap.containsKey("aid")) {
168 newAid = (String) resultMap.get("aid");
173 String consistency = MusicUtil.EVENTUAL;// for now this needs only
177 PreparedQueryObject queryObject = new PreparedQueryObject();
178 boolean result = false;
179 long start = System.currentTimeMillis();
180 Map<String, Object> replicationInfo = kspObject.getReplicationInfo();
181 String repString = "{" + MusicUtil.jsonMaptoSqlString(replicationInfo, ",") + "}";
182 queryObject.appendQueryString(
183 "CREATE KEYSPACE " + keyspaceName + " WITH replication = " + repString);
184 if (kspObject.getDurabilityOfWrites() != null) {
185 queryObject.appendQueryString(
186 " AND durable_writes = " + kspObject.getDurabilityOfWrites());
189 queryObject.appendQueryString(";");
190 long end = System.currentTimeMillis();
191 logger.info(EELFLoggerDelegate.applicationLogger,
192 "Time taken for setting up query in create keyspace:" + (end - start));
195 result = MusicCore.nonKeyRelatedPut(queryObject, consistency);
196 logger.error(EELFLoggerDelegate.errorLogger, "resulta = " + result);
197 } catch (Exception e) {
198 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage());
199 resultMap.put("Exception", "Couldn't create keyspace. Please make sure all the information is correct.");
202 logger.debug("result = " + result);
204 resultMap.put("Status", String.valueOf(result));
205 if(!resultMap.containsKey("Exception"))
206 resultMap.put("Exception", "Keyspace already exists. Please contact admin.");
207 if (resultMap.get("uuid").equals("new")) {
208 queryObject = new PreparedQueryObject();
209 queryObject.appendQueryString(
210 "DELETE FROM admin.keyspace_master where uuid = " + newAid);
211 queryObject.appendQueryString(";");
213 result = MusicCore.nonKeyRelatedPut(queryObject, consistency);
214 } catch (MusicServiceException e) {
215 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage());
216 resultMap.put("Exception", e.getMessage());
218 resultMap.remove("aid");
219 resultMap.remove("uuid");
223 queryObject = new PreparedQueryObject();
224 queryObject.appendQueryString(
225 "UPDATE admin.keyspace_master SET keyspace_name=? where uuid = ?;");
227 queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(),
228 MusicUtil.DEFAULTKEYSPACENAME));
229 queryObject.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), newAid));
230 } catch (Exception e) {
231 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage());
232 resultMap.put("Exception", "Unable to process input data. Invalid input data type. "
233 + "Please check keyspace_name and aid.. ");
236 result = MusicCore.nonKeyRelatedPut(queryObject, consistency);
237 } catch (MusicServiceException e) {
239 logger.error(EELFLoggerDelegate.errorLogger, "Unable to process operation. Error: "+e.getMessage());
240 resultMap.put("Exception", "Unable to process operation. Error: "+e.getMessage());
243 resultMap.remove("aid");
244 resultMap.remove("uuid");
250 queryObject = new PreparedQueryObject();
251 queryObject.appendQueryString("CREATE ROLE IF NOT EXISTS '" + userId
252 + "' WITH PASSWORD = '" + password + "' AND LOGIN = true;");
253 result = MusicCore.nonKeyRelatedPut(queryObject, consistency);
254 queryObject = new PreparedQueryObject();
255 queryObject.appendQueryString("GRANT ALL PERMISSIONS on KEYSPACE " + keyspaceName
256 + " to '" + userId + "'");
257 queryObject.appendQueryString(";");
258 result = MusicCore.nonKeyRelatedPut(queryObject, consistency);
259 } catch (Exception e) {
260 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage());
262 resultMap.remove("uuid");
264 if (CachingUtil.isAAFApplication(ns))
265 resultMap.remove("aid");
266 } catch (MusicServiceException e) {
268 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage());
269 resultMap.put("Exception", e.getMessage());
272 resultMap.put("Status", String.valueOf(result));
280 * @param keyspaceName
286 @ApiOperation(value = "Delete Keyspace", response = String.class)
287 @Consumes(MediaType.APPLICATION_JSON)
288 @Produces(MediaType.APPLICATION_JSON)
289 public Map<String, Object> dropKeySpace(
290 @ApiParam(value = "Major Version",
291 required = true) @PathParam("version") String version,
292 @ApiParam(value = "Minor Version",
293 required = false) @HeaderParam("X-minorVersion") String minorVersion,
294 @ApiParam(value = "Patch Version",
295 required = false) @HeaderParam("X-patchVersion") String patchVersion,
296 @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid,
297 @ApiParam(value = "Application namespace",
298 required = true) @HeaderParam("ns") String ns,
299 @ApiParam(value = "userId",
300 required = true) @HeaderParam("userId") String userId,
301 @ApiParam(value = "Password",
302 required = true) @HeaderParam("password") String password,
303 JsonKeySpace kspObject,
304 @ApiParam(value = "Keyspace Name",
305 required = true) @PathParam("name") String keyspaceName,
306 @Context HttpServletResponse response) throws Exception {
307 Map<String, Object> resultMap = MusicCore.autheticateUser(ns, userId, password,
308 keyspaceName, aid, "dropKeySpace");
309 response.addHeader(xLatestVersion, MusicUtil.getVersion());
310 if (resultMap.containsKey("aid"))
311 resultMap.remove("aid");
312 if (!resultMap.isEmpty()) {
316 String consistency = MusicUtil.EVENTUAL;// for now this needs only
319 String appName = CachingUtil.getAppName(keyspaceName);
320 String uuid = CachingUtil.getUuidFromMusicCache(keyspaceName);
321 PreparedQueryObject pQuery = new PreparedQueryObject();
322 pQuery.appendQueryString(
323 "select count(*) as count from admin.keyspace_master where application_name=? allow filtering;");
324 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName));
325 Row row = MusicCore.get(pQuery).one();
326 long count = row.getLong(0);
329 resultMap.put("Exception", "Keyspace not found. Please make sure keyspace exists.");
331 } else if (count == 1) {
332 pQuery = new PreparedQueryObject();
333 pQuery.appendQueryString(
334 "UPDATE admin.keyspace_master SET keyspace_name=? where uuid = ?;");
335 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(),
336 MusicUtil.DEFAULTKEYSPACENAME));
337 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), uuid));
338 MusicCore.nonKeyRelatedPut(pQuery, consistency);
340 pQuery = new PreparedQueryObject();
341 pQuery.appendQueryString("delete from admin.keyspace_master where uuid = ?");
342 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), uuid));
343 MusicCore.nonKeyRelatedPut(pQuery, consistency);
346 PreparedQueryObject queryObject = new PreparedQueryObject();
347 queryObject.appendQueryString("DROP KEYSPACE " + keyspaceName + ";");
348 return new JsonResponse(MusicCore.nonKeyRelatedPut(queryObject, consistency), "", "")
361 @Path("/{keyspace}/tables/{tablename}")
362 @ApiOperation(value = "Create Table", response = String.class)
363 @Consumes(MediaType.APPLICATION_JSON)
364 @Produces(MediaType.APPLICATION_JSON)
365 public Map<String, Object> createTable(
366 @ApiParam(value = "Major Version",
367 required = true) @PathParam("version") String version,
368 @ApiParam(value = "Minor Version",
369 required = false) @HeaderParam("X-minorVersion") String minorVersion,
370 @ApiParam(value = "Patch Version",
371 required = false) @HeaderParam("X-patchVersion") String patchVersion,
372 @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid,
373 @ApiParam(value = "Application namespace",
374 required = true) @HeaderParam("ns") String ns,
375 @ApiParam(value = "userId",
376 required = true) @HeaderParam("userId") String userId,
377 @ApiParam(value = "Password",
378 required = true) @HeaderParam("password") String password,
380 @ApiParam(value = "Keyspace Name",
381 required = true) @PathParam("keyspace") String keyspace,
382 @ApiParam(value = "Table Name",
383 required = true) @PathParam("tablename") String tablename,
384 @Context HttpServletResponse response) throws Exception {
385 Map<String, Object> resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace,
387 response.addHeader(xLatestVersion, MusicUtil.getVersion());
388 if (resultMap.containsKey("aid"))
389 resultMap.remove("aid");
390 if (!resultMap.isEmpty()) {
393 String consistency = MusicUtil.EVENTUAL;
394 // for now this needs only eventual consistency
395 PreparedQueryObject queryObject = new PreparedQueryObject();
396 boolean result = false;
397 // first read the information about the table fields
398 Map<String, String> fields = tableObj.getFields();
399 StringBuilder fieldsString = new StringBuilder("(vector_ts text,");
402 for (Map.Entry<String, String> entry : fields.entrySet()) {
404 if (entry.getKey().equals("PRIMARY KEY")) {
405 if(! entry.getValue().contains("("))
406 primaryKey = entry.getValue();
408 primaryKey = entry.getValue().substring(entry.getValue().indexOf('(') + 1);
409 primaryKey = primaryKey.substring(0, primaryKey.indexOf(')'));
411 fieldsString.append("" + entry.getKey() + " (" + primaryKey + ")");
413 fieldsString.append("" + entry.getKey() + " " + entry.getValue() + "");
414 if (counter == fields.size() - 1)
415 fieldsString.append(")");
417 fieldsString.append(",");
418 counter = counter + 1;
420 // information about the name-value style properties
421 Map<String, Object> propertiesMap = tableObj.getProperties();
422 StringBuilder propertiesString = new StringBuilder();
423 if (propertiesMap != null) {
425 for (Map.Entry<String, Object> entry : propertiesMap.entrySet()) {
426 Object ot = entry.getValue();
427 String value = ot + "";
428 if (ot instanceof String) {
429 value = "'" + value + "'";
430 } else if (ot instanceof Map) {
431 @SuppressWarnings("unchecked")
432 Map<String, Object> otMap = (Map<String, Object>) ot;
433 value = "{" + MusicUtil.jsonMaptoSqlString(otMap, ",") + "}";
436 propertiesString.append(entry.getKey() + "=" + value + "");
437 if (counter != propertiesMap.size() - 1)
438 propertiesString.append(" AND ");
440 counter = counter + 1;
444 queryObject.appendQueryString(
445 "CREATE TABLE " + keyspace + "." + tablename + " " + fieldsString);
447 if (propertiesMap != null)
448 queryObject.appendQueryString(" WITH " + propertiesString);
450 queryObject.appendQueryString(";");
452 result = MusicCore.nonKeyRelatedPut(queryObject, consistency);
453 } catch (MusicServiceException ex) {
454 return new JsonResponse(false, ex.getMessage(), "").toMap();
457 return new JsonResponse(result, "", "").toMap();
469 @Path("/{keyspace}/tables/{tablename}/index/{field}")
470 @ApiOperation(value = "Create Index", response = String.class)
471 @Produces(MediaType.APPLICATION_JSON)
472 public Map<String, Object> createIndex(
473 @ApiParam(value = "Major Version",
474 required = true) @PathParam("version") String version,
475 @ApiParam(value = "Minor Version",
476 required = false) @HeaderParam("X-minorVersion") String minorVersion,
477 @ApiParam(value = "Patch Version",
478 required = false) @HeaderParam("X-patchVersion") String patchVersion,
479 @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid,
480 @ApiParam(value = "Application namespace",
481 required = true) @HeaderParam("ns") String ns,
482 @ApiParam(value = "userId",
483 required = true) @HeaderParam("userId") String userId,
484 @ApiParam(value = "Password",
485 required = true) @HeaderParam("password") String password,
486 @ApiParam(value = "Keyspace Name",
487 required = true) @PathParam("keyspace") String keyspace,
488 @ApiParam(value = "Table Name",
489 required = true) @PathParam("tablename") String tablename,
490 @ApiParam(value = "Field Name",
491 required = true) @PathParam("field") String fieldName,
492 @Context UriInfo info, @Context HttpServletResponse response) throws Exception {
493 Map<String, Object> resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace,
495 response.addHeader(xLatestVersion, MusicUtil.getVersion());
496 if (resultMap.containsKey("aid"))
497 resultMap.remove("aid");
498 if (!resultMap.isEmpty())
500 MultivaluedMap<String, String> rowParams = info.getQueryParameters();
501 String indexName = "";
502 if (rowParams.getFirst("index_name") != null)
503 indexName = rowParams.getFirst("index_name");
504 PreparedQueryObject query = new PreparedQueryObject();
505 query.appendQueryString("Create index " + indexName + " if not exists on " + keyspace + "."
506 + tablename + " (" + fieldName + ");");
507 return new JsonResponse(MusicCore.nonKeyRelatedPut(query, "eventual"), "", "").toMap();
520 @Path("/{keyspace}/tables/{tablename}/rows")
521 @ApiOperation(value = "Insert Into Table", response = String.class)
522 @Consumes(MediaType.APPLICATION_JSON)
523 @Produces(MediaType.APPLICATION_JSON)
524 public Map<String, Object> insertIntoTable(
525 @ApiParam(value = "Major Version",
526 required = true) @PathParam("version") String version,
527 @ApiParam(value = "Minor Version",
528 required = false) @HeaderParam("X-minorVersion") String minorVersion,
529 @ApiParam(value = "Patch Version",
530 required = false) @HeaderParam("X-patchVersion") String patchVersion,
531 @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid,
532 @ApiParam(value = "Application namespace",
533 required = true) @HeaderParam("ns") String ns,
534 @ApiParam(value = "userId",
535 required = true) @HeaderParam("userId") String userId,
536 @ApiParam(value = "Password",
537 required = true) @HeaderParam("password") String password,
539 @ApiParam(value = "Keyspace Name",
540 required = true) @PathParam("keyspace") String keyspace,
541 @ApiParam(value = "Table Name",
542 required = true) @PathParam("tablename") String tablename,
543 @Context HttpServletResponse response) {
544 Map<String, Object> resultMap = null;
546 resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace,
547 aid, "insertIntoTable");
548 } catch (Exception e) {
549 logger.error(EELFLoggerDelegate.errorLogger,e.getMessage());
550 return new ReturnType(ResultType.FAILURE, e.getMessage()).toMap();
552 response.addHeader(xLatestVersion, MusicUtil.getVersion());
553 if (resultMap.containsKey("aid"))
554 resultMap.remove("aid");
555 if (!resultMap.isEmpty()) {
558 ReturnType result = null;
559 Map<String, Object> valuesMap = insObj.getValues();
560 PreparedQueryObject queryObject = new PreparedQueryObject();
561 TableMetadata tableInfo = null;
563 tableInfo = MusicCore.returnColumnMetadata(keyspace, tablename);
564 if(tableInfo == null)
565 throw new MusicServiceException("Table name doesn't exists. Please check the table name.");
566 } catch (MusicServiceException e) {
568 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage());
569 resultMap.put(ResultType.SYNTAXERROR.getResult(), e.getMessage());
572 String primaryKeyName = tableInfo.getPrimaryKey().get(0).getName();
573 StringBuilder fieldsString = new StringBuilder("(vector_ts,");
575 String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis());
576 StringBuilder valueString = new StringBuilder("(" + "?" + ",");
577 queryObject.addValue(vectorTs);
579 String primaryKey = "";
581 for (Map.Entry<String, Object> entry : valuesMap.entrySet()) {
582 fieldsString.append("" + entry.getKey());
583 Object valueObj = entry.getValue();
584 if (primaryKeyName.equals(entry.getKey())) {
585 primaryKey = entry.getValue() + "";
586 primaryKey = primaryKey.replace("'", "''");
589 DataType colType = tableInfo.getColumn(entry.getKey()).getType();
591 Object formattedValue = null;
593 formattedValue = MusicUtil.convertToActualDataType(colType, valueObj);
594 } catch (Exception e) {
595 logger.error(EELFLoggerDelegate.errorLogger,e.getMessage());
596 //return new ReturnType(ResultType.FAILURE, e.getMessage()).toMap();
598 valueString.append("?");
599 queryObject.addValue(formattedValue);
601 if (counter == valuesMap.size() - 1) {
602 fieldsString.append(")");
603 valueString.append(")");
605 fieldsString.append(",");
606 valueString.append(",");
608 counter = counter + 1;
611 queryObject.appendQueryString("INSERT INTO " + keyspace + "." + tablename + " "
612 + fieldsString + " VALUES " + valueString);
614 String ttl = insObj.getTtl();
615 String timestamp = insObj.getTimestamp();
617 if ((ttl != null) && (timestamp != null)) {
618 logger.info(EELFLoggerDelegate.applicationLogger, "both there");
619 queryObject.appendQueryString(" USING TTL ? AND TIMESTAMP ?");
620 queryObject.addValue(Integer.parseInt(ttl));
621 queryObject.addValue(Long.parseLong(timestamp));
624 if ((ttl != null) && (timestamp == null)) {
625 logger.info(EELFLoggerDelegate.applicationLogger, "ONLY TTL there");
626 queryObject.appendQueryString(" USING TTL ?");
627 queryObject.addValue(Integer.parseInt(ttl));
630 if ((ttl == null) && (timestamp != null)) {
631 logger.info(EELFLoggerDelegate.applicationLogger, "ONLY timestamp there");
632 queryObject.appendQueryString(" USING TIMESTAMP ?");
633 queryObject.addValue(Long.parseLong(timestamp));
636 queryObject.appendQueryString(";");
638 String consistency = insObj.getConsistencyInfo().get("type");
640 if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL)) {
641 result = MusicCore.eventualPut(queryObject);
642 } else if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) {
643 String lockId = insObj.getConsistencyInfo().get("lockId");
644 result = MusicCore.criticalPut(keyspace, tablename, primaryKey, queryObject, lockId,
646 } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) {
647 result = MusicCore.atomicPut(keyspace, tablename, primaryKey, queryObject, null);
650 else if (consistency.equalsIgnoreCase(MusicUtil.ATOMICDELETELOCK)) {
651 result = MusicCore.atomicPutWithDeleteLock(keyspace, tablename, primaryKey, queryObject, null);
654 return (result != null) ? result.toMap()
655 : new ReturnType(ResultType.FAILURE,
656 "Null result - Please Contact admin").toMap();
657 } catch (Exception ex) {
658 logger.error(EELFLoggerDelegate.applicationLogger, ex.getMessage());
659 return new ReturnType(ResultType.FAILURE, ex.getMessage()).toMap();
673 @Path("/{keyspace}/tables/{tablename}/rows")
674 @ApiOperation(value = "Update Table", response = String.class)
675 @Consumes(MediaType.APPLICATION_JSON)
676 @Produces(MediaType.APPLICATION_JSON)
677 public Map<String, Object> updateTable(
678 @ApiParam(value = "Major Version",
679 required = true) @PathParam("version") String version,
680 @ApiParam(value = "Minor Version",
681 required = false) @HeaderParam("X-minorVersion") String minorVersion,
682 @ApiParam(value = "Patch Version",
683 required = false) @HeaderParam("X-patchVersion") String patchVersion,
684 @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid,
685 @ApiParam(value = "Application namespace",
686 required = true) @HeaderParam("ns") String ns,
687 @ApiParam(value = "userId",
688 required = true) @HeaderParam("userId") String userId,
689 @ApiParam(value = "Password",
690 required = true) @HeaderParam("password") String password,
691 JsonUpdate updateObj,
692 @ApiParam(value = "Keyspace Name",
693 required = true) @PathParam("keyspace") String keyspace,
694 @ApiParam(value = "Table Name",
695 required = true) @PathParam("tablename") String tablename,
696 @Context UriInfo info, @Context HttpServletResponse response) {
697 Map<String, Object> resultMap;
699 resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace,
701 } catch (Exception e) {
702 logger.error(EELFLoggerDelegate.errorLogger,e.getMessage());
703 return new ReturnType(ResultType.FAILURE, e.getMessage()).toMap();
705 response.addHeader(xLatestVersion, MusicUtil.getVersion());
706 if (resultMap.containsKey("aid"))
707 resultMap.remove("aid");
708 if (!resultMap.isEmpty()) {
711 long startTime = System.currentTimeMillis();
712 String operationId = UUID.randomUUID().toString();// just for infoging
714 String consistency = updateObj.getConsistencyInfo().get("type");
715 logger.info(EELFLoggerDelegate.applicationLogger, "--------------Music " + consistency
716 + " update-" + operationId + "-------------------------");
717 // obtain the field value pairs of the update
719 PreparedQueryObject queryObject = new PreparedQueryObject();
720 Map<String, Object> valuesMap = updateObj.getValues();
722 TableMetadata tableInfo;
724 tableInfo = MusicCore.returnColumnMetadata(keyspace, tablename);
725 } catch (MusicServiceException e) {
727 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage());
728 resultMap.put("Exception", e.getMessage());
731 if (tableInfo == null) {
732 return new ReturnType(ResultType.FAILURE,
733 "Table information not found. Please check input for table name= "
734 + keyspace + "." + tablename).toMap();
737 String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis());
738 StringBuilder fieldValueString = new StringBuilder("vector_ts=?,");
739 queryObject.addValue(vectorTs);
741 for (Map.Entry<String, Object> entry : valuesMap.entrySet()) {
742 Object valueObj = entry.getValue();
743 DataType colType = tableInfo.getColumn(entry.getKey()).getType();
744 Object valueString = null;
746 valueString = MusicUtil.convertToActualDataType(colType, valueObj);
747 } catch (Exception e) {
748 logger.error(EELFLoggerDelegate.errorLogger,e.getMessage());
749 //return new ReturnType(ResultType.FAILURE, e.getMessage()).toMap();
751 fieldValueString.append(entry.getKey() + "= ?");
752 queryObject.addValue(valueString);
753 if (counter != valuesMap.size() - 1)
754 fieldValueString.append(",");
755 counter = counter + 1;
757 String ttl = updateObj.getTtl();
758 String timestamp = updateObj.getTimestamp();
760 queryObject.appendQueryString("UPDATE " + keyspace + "." + tablename + " ");
761 if ((ttl != null) && (timestamp != null)) {
763 logger.info("both there");
764 queryObject.appendQueryString(" USING TTL ? AND TIMESTAMP ?");
765 queryObject.addValue(Integer.parseInt(ttl));
766 queryObject.addValue(Long.parseLong(timestamp));
769 if ((ttl != null) && (timestamp == null)) {
770 logger.info("ONLY TTL there");
771 queryObject.appendQueryString(" USING TTL ?");
772 queryObject.addValue(Integer.parseInt(ttl));
775 if ((ttl == null) && (timestamp != null)) {
776 logger.info("ONLY timestamp there");
777 queryObject.appendQueryString(" USING TIMESTAMP ?");
778 queryObject.addValue(Long.parseLong(timestamp));
780 // get the row specifier
781 RowIdentifier rowId = null;
783 rowId = getRowIdentifier(keyspace, tablename, info.getQueryParameters(), queryObject);
784 if(rowId == null || rowId.primarKeyValue.isEmpty()) {
785 resultMap.put(ResultType.SYNTAXERROR.getResult(), "Mandatory WHERE clause is missing. Please check the input request.");
788 } catch (MusicServiceException ex) {
789 logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage());
790 return new ReturnType(ResultType.FAILURE, ex.getMessage()).toMap();
793 queryObject.appendQueryString(
794 " SET " + fieldValueString + " WHERE " + rowId.rowIdString + ";");
796 // get the conditional, if any
797 Condition conditionInfo;
798 if (updateObj.getConditions() == null)
799 conditionInfo = null;
800 else {// to avoid parsing repeatedly, just send the select query to
802 PreparedQueryObject selectQuery = new PreparedQueryObject();
803 selectQuery.appendQueryString("SELECT * FROM " + keyspace + "." + tablename + " WHERE "
804 + rowId.rowIdString + ";");
805 selectQuery.addValue(rowId.primarKeyValue);
806 conditionInfo = new MusicCore.Condition(updateObj.getConditions(), selectQuery);
809 ReturnType operationResult = null;
810 long jsonParseCompletionTime = System.currentTimeMillis();
812 if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL))
813 operationResult = MusicCore.eventualPut(queryObject);
814 else if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) {
815 String lockId = updateObj.getConsistencyInfo().get("lockId");
816 operationResult = MusicCore.criticalPut(keyspace, tablename, rowId.primarKeyValue,
817 queryObject, lockId, conditionInfo);
818 } else if (consistency.equalsIgnoreCase("atomic_delete_lock")) {
819 // this function is mainly for the benchmarks
821 operationResult = MusicCore.atomicPutWithDeleteLock(keyspace, tablename,
822 rowId.primarKeyValue, queryObject, conditionInfo);
823 } catch (MusicLockingException e) {
824 logger.error(EELFLoggerDelegate.errorLogger,e.getMessage());
825 return new ReturnType(ResultType.FAILURE, e.getMessage()).toMap();
827 } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) {
829 operationResult = MusicCore.atomicPut(keyspace, tablename, rowId.primarKeyValue,
830 queryObject, conditionInfo);
831 } catch (MusicLockingException e) {
832 logger.error(EELFLoggerDelegate.errorLogger,e.getMessage());
833 return new ReturnType(ResultType.FAILURE, e.getMessage()).toMap();
836 long actualUpdateCompletionTime = System.currentTimeMillis();
838 long endTime = System.currentTimeMillis();
839 String timingString = "Time taken in ms for Music " + consistency + " update-" + operationId
840 + ":" + "|total operation time:" + (endTime - startTime)
841 + "|json parsing time:" + (jsonParseCompletionTime - startTime)
842 + "|update time:" + (actualUpdateCompletionTime - jsonParseCompletionTime)
845 if (operationResult != null && operationResult.getTimingInfo() != null) {
846 String lockManagementTime = operationResult.getTimingInfo();
847 timingString = timingString + lockManagementTime;
849 logger.info(EELFLoggerDelegate.applicationLogger, timingString);
850 return (operationResult != null) ? operationResult.toMap()
851 : new ReturnType(ResultType.FAILURE, "Null result - Please Contact admin")
865 @Path("/{keyspace}/tables/{tablename}/rows")
866 @ApiOperation(value = "Delete From table", response = String.class)
867 @Consumes(MediaType.APPLICATION_JSON)
868 @Produces(MediaType.APPLICATION_JSON)
869 public Map<String, Object> deleteFromTable(
870 @ApiParam(value = "Major Version",
871 required = true) @PathParam("version") String version,
872 @ApiParam(value = "Minor Version",
873 required = false) @HeaderParam("X-minorVersion") String minorVersion,
874 @ApiParam(value = "Patch Version",
875 required = false) @HeaderParam("X-patchVersion") String patchVersion,
876 @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid,
877 @ApiParam(value = "Application namespace",
878 required = true) @HeaderParam("ns") String ns,
879 @ApiParam(value = "userId",
880 required = true) @HeaderParam("userId") String userId,
881 @ApiParam(value = "Password",
882 required = true) @HeaderParam("password") String password,
884 @ApiParam(value = "Keyspace Name",
885 required = true) @PathParam("keyspace") String keyspace,
886 @ApiParam(value = "Table Name",
887 required = true) @PathParam("tablename") String tablename,
888 @Context UriInfo info, @Context HttpServletResponse response) {
889 Map<String, Object> resultMap = null;
891 resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace,
892 aid, "deleteFromTable");
893 } catch (Exception e) {
894 resultMap.put("Exception", e.getMessage());
897 response.addHeader(xLatestVersion, MusicUtil.getVersion());
898 if (resultMap.containsKey("aid"))
899 resultMap.remove("aid");
900 if (!resultMap.isEmpty()) {
904 resultMap.put("Exception", "Request body is missing. Please check your input data and retry.");
907 PreparedQueryObject queryObject = new PreparedQueryObject();
908 StringBuilder columnString = new StringBuilder();
911 ArrayList<String> columnList = delObj.getColumns();
912 if (columnList != null) {
913 for (String column : columnList) {
914 columnString.append(column);
915 if (counter != columnList.size() - 1)
916 columnString.append(",");
917 counter = counter + 1;
921 // get the row specifier
922 RowIdentifier rowId = null;
924 rowId = getRowIdentifier(keyspace, tablename, info.getQueryParameters(), queryObject);
925 } catch (MusicServiceException ex) {
926 return new ReturnType(ResultType.FAILURE, ex.getMessage()).toMap();
928 String rowSpec = rowId.rowIdString.toString();
930 if ((columnList != null) && (!rowSpec.isEmpty())) {
931 queryObject.appendQueryString("DELETE " + columnString + " FROM " + keyspace + "."
932 + tablename + " WHERE " + rowSpec + ";");
935 if ((columnList == null) && (!rowSpec.isEmpty())) {
936 queryObject.appendQueryString("DELETE FROM " + keyspace + "." + tablename + " WHERE "
940 if ((columnList != null) && (rowSpec.isEmpty())) {
941 queryObject.appendQueryString(
942 "DELETE " + columnString + " FROM " + keyspace + "." + rowSpec + ";");
945 // get the conditional, if any
946 Condition conditionInfo;
947 if (delObj.getConditions() == null)
948 conditionInfo = null;
949 else {// to avoid parsing repeatedly, just send the select query to
951 PreparedQueryObject selectQuery = new PreparedQueryObject();
952 selectQuery.appendQueryString("SELECT * FROM " + keyspace + "." + tablename + " WHERE "
953 + rowId.rowIdString + ";");
954 selectQuery.addValue(rowId.primarKeyValue);
955 conditionInfo = new MusicCore.Condition(delObj.getConditions(), selectQuery);
958 String consistency = delObj.getConsistencyInfo().get("type");
960 ReturnType operationResult = null;
962 if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL))
963 operationResult = MusicCore.eventualPut(queryObject);
964 else if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) {
965 String lockId = delObj.getConsistencyInfo().get("lockId");
966 operationResult = MusicCore.criticalPut(keyspace, tablename, rowId.primarKeyValue,
967 queryObject, lockId, conditionInfo);
968 } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) {
969 operationResult = MusicCore.atomicPut(keyspace, tablename, rowId.primarKeyValue,
970 queryObject, conditionInfo);
972 else if (consistency.equalsIgnoreCase(MusicUtil.ATOMICDELETELOCK)) {
973 operationResult = MusicCore.atomicPutWithDeleteLock(keyspace, tablename, rowId.primarKeyValue,
974 queryObject, conditionInfo);
976 } catch (MusicLockingException e) {
977 resultMap.put("Exception", "Unable to perform Delete operation. Exception from music: "+e.getMessage());
981 return operationResult.toMap();
982 } catch (NullPointerException e) {
983 return new ReturnType(ResultType.FAILURE, e.getMessage()).toMap();
995 @Path("/{keyspace}/tables/{tablename}")
996 @ApiOperation(value = "Drop Table", response = String.class)
997 @Consumes(MediaType.APPLICATION_JSON)
998 @Produces(MediaType.APPLICATION_JSON)
999 public Map<String, Object> dropTable(
1000 @ApiParam(value = "Major Version",
1001 required = true) @PathParam("version") String version,
1002 @ApiParam(value = "Minor Version",
1003 required = false) @HeaderParam("X-minorVersion") String minorVersion,
1004 @ApiParam(value = "Patch Version",
1005 required = false) @HeaderParam("X-patchVersion") String patchVersion,
1006 @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid,
1007 @ApiParam(value = "Application namespace",
1008 required = true) @HeaderParam("ns") String ns,
1009 @ApiParam(value = "userId",
1010 required = true) @HeaderParam("userId") String userId,
1011 @ApiParam(value = "Password",
1012 required = true) @HeaderParam("password") String password,
1014 @ApiParam(value = "Keyspace Name",
1015 required = true) @PathParam("keyspace") String keyspace,
1016 @ApiParam(value = "Table Name",
1017 required = true) @PathParam("tablename") String tablename,
1018 @Context HttpServletResponse response) throws Exception {
1019 Map<String, Object> resultMap =
1020 MusicCore.autheticateUser(ns, userId, password, keyspace, aid, "dropTable");
1021 response.addHeader(xLatestVersion, MusicUtil.getVersion());
1022 if (resultMap.containsKey("aid"))
1023 resultMap.remove("aid");
1024 if (!resultMap.isEmpty()) {
1027 String consistency = "eventual";// for now this needs only eventual
1029 PreparedQueryObject query = new PreparedQueryObject();
1030 query.appendQueryString("DROP TABLE " + keyspace + "." + tablename + ";");
1032 return new JsonResponse(MusicCore.nonKeyRelatedPut(query, consistency), "", "").toMap();
1033 } catch (MusicServiceException ex) {
1034 return new JsonResponse(false, ex.getMessage(), "").toMap();
1048 @Path("/{keyspace}/tables/{tablename}/rows/criticalget")
1049 @ApiOperation(value = "Select Critical", response = Map.class)
1050 @Consumes(MediaType.APPLICATION_JSON)
1051 @Produces(MediaType.APPLICATION_JSON)
1052 public Map<String, HashMap<String, Object>> selectCritical(
1053 @ApiParam(value = "Major Version",
1054 required = true) @PathParam("version") String version,
1055 @ApiParam(value = "Minor Version",
1056 required = false) @HeaderParam("X-minorVersion") String minorVersion,
1057 @ApiParam(value = "Patch Version",
1058 required = false) @HeaderParam("X-patchVersion") String patchVersion,
1059 @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid,
1060 @ApiParam(value = "Application namespace",
1061 required = true) @HeaderParam("ns") String ns,
1062 @ApiParam(value = "userId",
1063 required = true) @HeaderParam("userId") String userId,
1064 @ApiParam(value = "Password",
1065 required = true) @HeaderParam("password") String password,
1067 @ApiParam(value = "Keyspace Name",
1068 required = true) @PathParam("keyspace") String keyspace,
1069 @ApiParam(value = "Table Name",
1070 required = true) @PathParam("tablename") String tablename,
1071 @Context UriInfo info, @Context HttpServletResponse response) throws Exception {
1072 Map<String, Object> resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace,
1073 aid, "selectCritical");
1074 response.addHeader(xLatestVersion, MusicUtil.getVersion());
1075 if (resultMap.containsKey("aid"))
1076 resultMap.remove("aid");
1077 if (!resultMap.isEmpty()) {
1078 logger.error("Error while authentication... ");
1079 HashMap<String, Object> tempMap = new HashMap<>();
1080 tempMap.putAll(resultMap);
1081 Map<String, HashMap<String, Object>> results = new HashMap<>();
1082 results.put("Result", tempMap);
1085 String lockId = selObj.getConsistencyInfo().get("lockId");
1087 PreparedQueryObject queryObject = new PreparedQueryObject();
1089 RowIdentifier rowId = null;
1091 rowId = getRowIdentifier(keyspace, tablename, info.getQueryParameters(), queryObject);
1092 } catch (MusicServiceException ex) {
1093 return MusicUtil.setErrorResponse(ex);
1095 queryObject.appendQueryString(
1096 "SELECT * FROM " + keyspace + "." + tablename + " WHERE " + rowId.rowIdString + ";");
1098 ResultSet results = null;
1100 String consistency = selObj.getConsistencyInfo().get("type");
1102 if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) {
1103 results = MusicCore.criticalGet(keyspace, tablename, rowId.primarKeyValue, queryObject,
1105 } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) {
1106 results = MusicCore.atomicGet(keyspace, tablename, rowId.primarKeyValue, queryObject);
1109 else if (consistency.equalsIgnoreCase(MusicUtil.ATOMICDELETELOCK)) {
1110 results = MusicCore.atomicGetWithDeleteLock(keyspace, tablename, rowId.primarKeyValue, queryObject);
1113 return MusicCore.marshallResults(results);
1125 @Path("/{keyspace}/tables/{tablename}/rows")
1126 @ApiOperation(value = "Select All or Select Specivic", response = Map.class)
1127 @Produces(MediaType.APPLICATION_JSON)
1128 public Map<String, HashMap<String, Object>> select(
1129 @ApiParam(value = "Major Version",
1130 required = true) @PathParam("version") String version,
1131 @ApiParam(value = "Minor Version",
1132 required = false) @HeaderParam("X-minorVersion") String minorVersion,
1133 @ApiParam(value = "Patch Version",
1134 required = false) @HeaderParam("X-patchVersion") String patchVersion,
1135 @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid,
1136 @ApiParam(value = "Application namespace",
1137 required = true) @HeaderParam("ns") String ns,
1138 @ApiParam(value = "userId",
1139 required = true) @HeaderParam("userId") String userId,
1140 @ApiParam(value = "Password",
1141 required = true) @HeaderParam("password") String password,
1142 @ApiParam(value = "Keyspace Name",
1143 required = true) @PathParam("keyspace") String keyspace,
1144 @ApiParam(value = "Table Name",
1145 required = true) @PathParam("tablename") String tablename,
1146 @Context UriInfo info, @Context HttpServletResponse response) throws Exception {
1147 Map<String, Object> resultMap =
1148 MusicCore.autheticateUser(ns, userId, password, keyspace, aid, "select");
1149 response.addHeader(xLatestVersion, MusicUtil.getVersion());
1150 if (resultMap.containsKey("aid"))
1151 resultMap.remove("aid");
1152 if (!resultMap.isEmpty()) {
1153 logger.error("Error while authentication... ");
1154 HashMap<String, Object> tempMap = new HashMap<>();
1155 tempMap.putAll(resultMap);
1156 Map<String, HashMap<String, Object>> results = new HashMap<>();
1157 results.put("Result", tempMap);
1160 PreparedQueryObject queryObject = new PreparedQueryObject();
1162 if (info.getQueryParameters().isEmpty())// select all
1163 queryObject.appendQueryString("SELECT * FROM " + keyspace + "." + tablename + ";");
1165 int limit = -1; // do not limit the number of results
1167 queryObject = selectSpecificQuery(version, minorVersion, patchVersion, aid, ns,
1168 userId, password, keyspace, tablename, info, limit);
1169 } catch (MusicServiceException ex) {
1170 return MusicUtil.setErrorResponse(ex);
1175 ResultSet results = MusicCore.get(queryObject);
1176 return MusicCore.marshallResults(results);
1177 } catch (MusicServiceException ex) {
1178 return MusicUtil.setErrorResponse(ex);
1190 * @throws MusicServiceException
1192 public PreparedQueryObject selectSpecificQuery(String version, String minorVersion,
1193 String patchVersion, String aid, String ns, String userId, String password,
1194 String keyspace, String tablename, UriInfo info, int limit)
1195 throws MusicServiceException {
1197 PreparedQueryObject queryObject = new PreparedQueryObject();
1198 StringBuilder rowIdString = getRowIdentifier(keyspace, tablename, info.getQueryParameters(),
1199 queryObject).rowIdString;
1201 queryObject.appendQueryString(
1202 "SELECT * FROM " + keyspace + "." + tablename + " WHERE " + rowIdString);
1205 queryObject.appendQueryString(" LIMIT " + limit);
1208 queryObject.appendQueryString(";");
1218 * @param queryObject
1220 * @throws MusicServiceException
1222 private RowIdentifier getRowIdentifier(String keyspace, String tablename,
1223 MultivaluedMap<String, String> rowParams, PreparedQueryObject queryObject)
1224 throws MusicServiceException {
1225 StringBuilder rowSpec = new StringBuilder();
1227 TableMetadata tableInfo = MusicCore.returnColumnMetadata(keyspace, tablename);
1228 if (tableInfo == null) {
1229 logger.error(EELFLoggerDelegate.errorLogger,
1230 "Table information not found. Please check input for table name= "
1231 + keyspace + "." + tablename);
1232 throw new MusicServiceException(
1233 "Table information not found. Please check input for table name= "
1234 + keyspace + "." + tablename);
1236 StringBuilder primaryKey = new StringBuilder();
1237 for (MultivaluedMap.Entry<String, List<String>> entry : rowParams.entrySet()) {
1238 String keyName = entry.getKey();
1239 List<String> valueList = entry.getValue();
1240 String indValue = valueList.get(0);
1241 DataType colType = tableInfo.getColumn(entry.getKey()).getType();
1242 Object formattedValue = null;
1244 formattedValue = MusicUtil.convertToActualDataType(colType, indValue);
1245 } catch (Exception e) {
1246 logger.error(EELFLoggerDelegate.errorLogger,e.getMessage());
1248 primaryKey.append(indValue);
1249 rowSpec.append(keyName + "= ?");
1250 queryObject.addValue(formattedValue);
1251 if (counter != rowParams.size() - 1)
1252 rowSpec.append(" AND ");
1253 counter = counter + 1;
1255 return new RowIdentifier(primaryKey.toString(), rowSpec, queryObject);