Merge "Added fix for potential nullpointerexception"
[music.git] / src / main / java / org / onap / music / rest / RestMusicAdminAPI.java
1 /*
2  * ============LICENSE_START==========================================
3  * org.onap.music
4  * ===================================================================
5  *  Copyright (c) 2017 AT&T Intellectual Property
6  * ===================================================================
7  *  Licensed under the Apache License, Version 2.0 (the "License");
8  *  you may not use this file except in compliance with the License.
9  *  You may obtain a copy of the License at
10  * 
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  *  Unless required by applicable law or agreed to in writing, software
14  *  distributed under the License is distributed on an "AS IS" BASIS,
15  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  *  See the License for the specific language governing permissions and
17  *  limitations under the License.
18  * 
19  * ============LICENSE_END=============================================
20  * ====================================================================
21  */
22 package org.onap.music.rest;
23
24
25 import java.util.Arrays;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.nio.ByteBuffer;
31 import java.nio.charset.Charset;
32 import java.util.ArrayList;
33 import java.util.Map;
34 import java.util.Map.Entry;
35 import java.util.Set;
36 import java.util.UUID;
37
38 import javax.ws.rs.Consumes;
39 import javax.ws.rs.DELETE;
40 import javax.ws.rs.POST;
41 import javax.ws.rs.PUT;
42 import javax.ws.rs.Path;
43 import javax.ws.rs.Produces;
44 import javax.ws.rs.core.MediaType;
45 import javax.ws.rs.core.Response;
46 import javax.ws.rs.core.Response.ResponseBuilder;
47 import javax.ws.rs.core.Response.Status;
48
49 import org.codehaus.jackson.map.ObjectMapper;
50 import org.mindrot.jbcrypt.BCrypt;
51 import org.onap.music.datastore.PreparedQueryObject;
52 import org.onap.music.datastore.jsonobjects.JSONCallbackResponse;
53 import org.onap.music.datastore.jsonobjects.JSONObject;
54 import org.onap.music.datastore.jsonobjects.JsonCallback;
55 import org.onap.music.datastore.jsonobjects.JsonNotification;
56 import org.onap.music.datastore.jsonobjects.JsonNotifyClientResponse;
57 import org.onap.music.datastore.jsonobjects.JsonOnboard;
58 import org.onap.music.eelf.logging.EELFLoggerDelegate;
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.main.CacheAccess;
63 import org.onap.music.main.CachingUtil;
64 import org.onap.music.main.MusicCore;
65 import org.onap.music.main.MusicUtil;
66 import org.onap.music.main.ResultType;
67 import org.onap.music.main.ReturnType;
68 import org.onap.music.response.jsonobjects.JsonResponse;
69
70 import com.datastax.driver.core.DataType;
71 import com.datastax.driver.core.ResultSet;
72 import com.datastax.driver.core.Row;
73 import com.datastax.driver.core.exceptions.InvalidQueryException;
74 import com.sun.jersey.api.client.Client;
75 import com.sun.jersey.api.client.ClientResponse;
76 import com.sun.jersey.api.client.WebResource;
77 import com.sun.jersey.core.util.Base64;
78
79 import io.swagger.annotations.Api;
80 import io.swagger.annotations.ApiOperation;
81 import com.datastax.driver.core.TableMetadata;
82
83 import javax.ws.rs.core.HttpHeaders;
84 import javax.ws.rs.core.MediaType;
85 import com.datastax.driver.core.ColumnDefinitions;
86 import com.datastax.driver.core.ColumnDefinitions.Definition;
87 import com.datastax.driver.core.TableMetadata;
88 //import java.util.Base64.Encoder; 
89 //import java.util.Base64.Decoder;
90
91 @Path("/v2/admin")
92 // @Path("/v{version: [0-9]+}/admin")
93 // @Path("/admin")
94 @Api(value = "Admin Api", hidden = true)
95 public class RestMusicAdminAPI {
96     private static EELFLoggerDelegate logger =
97                     EELFLoggerDelegate.getLogger(RestMusicAdminAPI.class);
98     /*
99      * API to onboard an application with MUSIC. This is the mandatory first step.
100      * 
101      */
102     @POST
103     @Path("/onboardAppWithMusic")
104     @ApiOperation(value = "Onboard application", response = String.class)
105     @Consumes(MediaType.APPLICATION_JSON)
106     @Produces(MediaType.APPLICATION_JSON)
107     public Response onboardAppWithMusic(JsonOnboard jsonObj) throws Exception {
108         ResponseBuilder response =
109                         Response.noContent().header("X-latestVersion", MusicUtil.getVersion());
110         Map<String, Object> resultMap = new HashMap<>();
111         String appName = jsonObj.getAppname();
112         String userId = jsonObj.getUserId();
113         String isAAF = jsonObj.getIsAAF();
114         String password = jsonObj.getPassword();
115         if (appName == null || userId == null || isAAF == null || password == null) {
116             logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MISSINGINFO,
117                             ErrorSeverity.CRITICAL, ErrorTypes.AUTHENTICATIONERROR);
118             resultMap.put("Exception",
119                             "Unauthorized: Please check the request parameters. Some of the required values appName(ns), userId, password, isAAF are missing.");
120             return Response.status(Status.UNAUTHORIZED).entity(resultMap).build();
121         }
122
123         PreparedQueryObject pQuery = new PreparedQueryObject();
124         pQuery.appendQueryString(
125                         "select uuid from admin.keyspace_master where application_name = ? allow filtering");
126         pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName));
127         ResultSet rs = MusicCore.get(pQuery);
128         if (!rs.all().isEmpty()) {
129             resultMap.put("Exception", "Application " + appName
130                             + " has already been onboarded. Please contact admin.");
131             return Response.status(Status.BAD_REQUEST).entity(resultMap).build();
132         }
133
134         pQuery = new PreparedQueryObject();
135         String uuid = CachingUtil.generateUUID();
136         pQuery.appendQueryString(
137                         "INSERT INTO admin.keyspace_master (uuid, keyspace_name, application_name, is_api, "
138                                         + "password, username, is_aaf) VALUES (?,?,?,?,?,?,?)");
139         pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), uuid));
140         pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(),
141                         MusicUtil.DEFAULTKEYSPACENAME));
142         pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName));
143         pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), "True"));
144         pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), BCrypt.hashpw(password, BCrypt.gensalt())));
145         pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), userId));
146         pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), isAAF));
147
148         String returnStr = MusicCore.eventualPut(pQuery).toString();
149         if (returnStr.contains("Failure")) {
150             resultMap.put("Exception",
151                             "Oops. Something wrong with onboarding process. Please retry later or contact admin.");
152             return Response.status(Status.BAD_REQUEST).entity(resultMap).build();
153         }
154         CachingUtil.updateisAAFCache(appName, isAAF);
155         resultMap.put("Success", "Your application " + appName + " has been onboarded with MUSIC.");
156         resultMap.put("Generated AID", uuid);
157         return Response.status(Status.OK).entity(resultMap).build();
158     }
159
160
161     @POST
162     @Path("/search")
163     @ApiOperation(value = "Search Onboard application", response = String.class)
164     @Consumes(MediaType.APPLICATION_JSON)
165     @Produces(MediaType.APPLICATION_JSON)
166     public Response getOnboardedInfoSearch(JsonOnboard jsonObj) throws Exception {
167         Map<String, Object> resultMap = new HashMap<>();
168         ResponseBuilder response =
169                         Response.noContent().header("X-latestVersion", MusicUtil.getVersion());
170         String appName = jsonObj.getAppname();
171         String uuid = jsonObj.getAid();
172         String isAAF = jsonObj.getIsAAF();
173
174         if (appName == null && uuid == null && isAAF == null) {
175             logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MISSINGINFO,
176                             ErrorSeverity.CRITICAL, ErrorTypes.AUTHENTICATIONERROR);
177             resultMap.put("Exception",
178                             "Unauthorized: Please check the request parameters. Enter atleast one of the following parameters: appName(ns), aid, isAAF.");
179             return Response.status(Status.BAD_REQUEST).entity(resultMap).build();
180         }
181
182         PreparedQueryObject pQuery = new PreparedQueryObject();
183         String cql = "select uuid, keyspace_name from admin.keyspace_master where ";
184         if (appName != null)
185             cql = cql + "application_name = ? AND ";
186         if (uuid != null)
187             cql = cql + "uuid = ? AND ";
188         if (isAAF != null)
189             cql = cql + "is_aaf = ?";
190
191         if (cql.endsWith("AND "))
192             cql = cql.trim().substring(0, cql.length() - 4);
193         logger.info("Query in callback is: " + cql);
194         cql = cql + " allow filtering";
195         pQuery.appendQueryString(cql);
196         if (appName != null)
197             pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName));
198         if (uuid != null)
199             pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), uuid));
200         if (isAAF != null)
201             pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(),
202                             Boolean.parseBoolean(isAAF)));
203         ResultSet rs = MusicCore.get(pQuery);
204         Iterator<Row> it = rs.iterator();
205         while (it.hasNext()) {
206             Row row = (Row) it.next();
207             resultMap.put(row.getUUID("uuid").toString(), row.getString("keyspace_name"));
208         }
209         if (resultMap.isEmpty()) {
210             if (uuid != null) {
211                 resultMap.put("Exception",
212                                 "Please make sure Aid is correct and application is onboarded.");
213                 return Response.status(Status.BAD_REQUEST).entity(resultMap).build();
214             } else {
215                 resultMap.put("Exception",
216                                 "Application is not onboarded. Please make sure all the information is correct.");
217                 return Response.status(Status.BAD_REQUEST).entity(resultMap).build();
218             }
219         }
220         return Response.status(Status.OK).entity(resultMap).build();
221     }
222
223
224     @DELETE
225     @Path("/onboardAppWithMusic")
226     @ApiOperation(value = "Delete Onboard application", response = String.class)
227     @Consumes(MediaType.APPLICATION_JSON)
228     @Produces(MediaType.APPLICATION_JSON)
229     public Response deleteOnboardApp(JsonOnboard jsonObj) throws Exception {
230         Map<String, Object> resultMap = new HashMap<>();
231         ResponseBuilder response =
232                         Response.noContent().header("X-latestVersion", MusicUtil.getVersion());
233         String appName = jsonObj.getAppname();
234         String aid = jsonObj.getAid();
235         PreparedQueryObject pQuery = new PreparedQueryObject();
236         String consistency = MusicUtil.EVENTUAL;;
237         if (appName == null && aid == null) {
238             logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MISSINGINFO,
239                             ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR);
240             resultMap.put("Exception", "Please make sure either appName(ns) or Aid is present");
241             return Response.status(Status.BAD_REQUEST).entity(resultMap).build();
242         }
243         if (aid != null) {
244             pQuery.appendQueryString(
245                             "SELECT keyspace_name FROM admin.keyspace_master WHERE uuid = ?");
246             pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(),
247                             UUID.fromString(aid)));
248             Row row = MusicCore.get(pQuery).one();
249             if (row != null) {
250                 String ks = row.getString("keyspace_name");
251                 if (!ks.equals(MusicUtil.DEFAULTKEYSPACENAME)) {
252                     PreparedQueryObject queryObject = new PreparedQueryObject();
253                     queryObject.appendQueryString("DROP KEYSPACE IF EXISTS " + ks + ";");
254                     MusicCore.nonKeyRelatedPut(queryObject, consistency);
255                 }
256             }
257             pQuery = new PreparedQueryObject();
258             pQuery.appendQueryString("delete from admin.keyspace_master where uuid = ? IF EXISTS");
259             pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(),
260                             UUID.fromString(aid)));
261             ResultType result = MusicCore.nonKeyRelatedPut(pQuery, consistency);
262             if (result == ResultType.SUCCESS) {
263                 resultMap.put("Success", "Your application has been deleted successfully");
264             } else {
265                 resultMap.put("Exception",
266                                 "Oops. Something went wrong. Please make sure Aid is correct or Application is onboarded");
267                 logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.INCORRECTDATA,
268                                 ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR);
269                 return Response.status(Status.BAD_REQUEST).entity(resultMap).build();
270
271             }
272             return Response.status(Status.OK).entity(resultMap).build();
273         }
274
275         pQuery.appendQueryString(
276                         "select uuid from admin.keyspace_master where application_name = ? allow filtering");
277         pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName));
278         ResultSet rs = MusicCore.get(pQuery);
279         List<Row> rows = rs.all();
280         String uuid = null;
281         if (rows.size() == 0) {
282             resultMap.put("Exception",
283                             "Application not found. Please make sure Application exists.");
284             logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.INCORRECTDATA,
285                             ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR);
286             return Response.status(Status.BAD_REQUEST).entity(resultMap).build();
287         } else if (rows.size() == 1) {
288             uuid = rows.get(0).getUUID("uuid").toString();
289             pQuery = new PreparedQueryObject();
290             pQuery.appendQueryString(
291                             "SELECT keyspace_name FROM admin.keyspace_master WHERE uuid = ?");
292             pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(),
293                             UUID.fromString(uuid)));
294             Row row = MusicCore.get(pQuery).one();
295             String ks = row.getString("keyspace_name");
296             if (!ks.equals(MusicUtil.DEFAULTKEYSPACENAME)) {
297                 PreparedQueryObject queryObject = new PreparedQueryObject();
298                 queryObject.appendQueryString("DROP KEYSPACE " + ks + ";");
299                 MusicCore.nonKeyRelatedPut(queryObject, consistency);
300             }
301
302             pQuery = new PreparedQueryObject();
303             pQuery.appendQueryString("delete from admin.keyspace_master where uuid = ?");
304             pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(),
305                             UUID.fromString(uuid)));
306             MusicCore.eventualPut(pQuery);
307             resultMap.put("Success", "Your application " + appName + " has been deleted.");
308             return Response.status(Status.OK).entity(resultMap).build();
309         } else {
310             resultMap.put("Failure",
311                             "More than one Aid exists for this application, so please provide Aid.");
312             logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MULTIPLERECORDS,
313                             ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR);
314             return Response.status(Status.BAD_REQUEST).entity(resultMap).build();
315         }
316     }
317
318
319     @PUT
320     @Path("/onboardAppWithMusic")
321     @ApiOperation(value = "Update Onboard application", response = String.class)
322     @Consumes(MediaType.APPLICATION_JSON)
323     @Produces(MediaType.APPLICATION_JSON)
324     public Response updateOnboardApp(JsonOnboard jsonObj) throws Exception {
325         Map<String, Object> resultMap = new HashMap<>();
326         ResponseBuilder response =
327                         Response.noContent().header("X-latestVersion", MusicUtil.getVersion());
328         String aid = jsonObj.getAid();
329         String appName = jsonObj.getAppname();
330         String userId = jsonObj.getUserId();
331         String isAAF = jsonObj.getIsAAF();
332         String password = jsonObj.getPassword();
333         String consistency = "eventual";
334         PreparedQueryObject pQuery;
335
336         if (aid == null) {
337             resultMap.put("Exception", "Please make sure Aid is present");
338             logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MISSINGDATA,
339                             ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR);
340             return Response.status(Status.BAD_REQUEST).entity(resultMap).build();
341         }
342
343         if (appName == null && userId == null && password == null && isAAF == null) {
344             resultMap.put("Exception",
345                             "No parameters found to update. Please update atleast one parameter.");
346             logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MISSINGDATA,
347                             ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR);
348             return Response.status(Status.BAD_REQUEST).entity(resultMap).build();
349         }
350
351         if (appName != null) {
352             pQuery = new PreparedQueryObject();
353             pQuery.appendQueryString(
354                             "select uuid from admin.keyspace_master where application_name = ? allow filtering");
355             pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName));
356             ResultSet rs = MusicCore.get(pQuery);
357             if (!rs.all().isEmpty()) {
358                 resultMap.put("Exception", "Application " + appName
359                                 + " has already been onboarded. Please contact admin.");
360                 logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.ALREADYEXIST,
361                                 ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR);
362                 return Response.status(Status.BAD_REQUEST).entity(resultMap).build();
363             }
364         }
365
366         pQuery = new PreparedQueryObject();
367         StringBuilder preCql = new StringBuilder("UPDATE admin.keyspace_master SET ");
368         if (appName != null)
369             preCql.append(" application_name = ?,");
370         if (userId != null)
371             preCql.append(" username = ?,");
372         if (password != null)
373             preCql.append(" password = ?,");
374         if (isAAF != null)
375             preCql.append(" is_aaf = ?,");
376         preCql.deleteCharAt(preCql.length() - 1);
377         preCql.append(" WHERE uuid = ? IF EXISTS");
378         pQuery.appendQueryString(preCql.toString());
379         if (appName != null)
380             pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName));
381         if (userId != null)
382             pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), userId));
383         if (password != null)
384             pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), BCrypt.hashpw(password, BCrypt.gensalt())));
385         if (isAAF != null)
386             pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), isAAF));
387
388         pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), UUID.fromString(aid)));
389         ResultType result = MusicCore.nonKeyRelatedPut(pQuery, consistency);
390
391         if (result == ResultType.SUCCESS) {
392             resultMap.put("Success", "Your application has been updated successfully");
393         } else {
394             resultMap.put("Exception",
395                             "Oops. Something went wrong. Please make sure Aid is correct and application is onboarded");
396             logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.INCORRECTDATA,
397                             ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR);
398             return Response.status(Status.BAD_REQUEST).entity(resultMap).build();
399         }
400
401         return Response.status(Status.OK).entity(resultMap).build();
402     }
403
404     Client client = Client.create();
405         ObjectMapper mapper = new ObjectMapper();
406         
407     @POST
408     @Path("/callbackOps")
409     @Produces(MediaType.APPLICATION_JSON)
410     @Consumes(MediaType.APPLICATION_JSON)
411         public Response callbackOps(final JSONObject inputJsonObj) {
412          // {"keyspace":"conductor","full_table":"conductor.plans","changeValue":{"conductor.plans.status":"Who??","position":"3"},"operation":"update","table_name":"plans","primary_key":"3"}
413         Map<String, Object> resultMap = new HashMap<>();
414         new Thread(new Runnable() {
415             public void run() {
416                 makeAsyncCall(inputJsonObj);
417             }
418         }).start();
419             
420                 return Response.status(Status.OK).entity(resultMap).build();
421         }
422     
423     private Response makeAsyncCall(JSONObject inputJsonObj) {
424         
425         Map<String, Object> resultMap = new HashMap<>();
426         try {
427                 logger.info(EELFLoggerDelegate.applicationLogger, "Got notification: " + inputJsonObj.getData());
428                         String dataStr = inputJsonObj.getData();
429                         JSONCallbackResponse jsonResponse = mapper.readValue(dataStr, JSONCallbackResponse.class);
430                         String operation = jsonResponse.getOperation();
431                         Map<String, String> changeValueMap = jsonResponse.getChangeValue();
432                         String primaryKey = jsonResponse.getPrimary_key();
433                         String ksTableName = jsonResponse.getFull_table(); //conductor.plans
434                         if(ksTableName.equals("admin.notification_master")) {
435                                 CachingUtil.updateCallbackNotifyList(new ArrayList<String>());
436                                 return Response.status(Status.OK).entity(resultMap).build();
437                         }
438                         List<String> inputUpdateList = jsonResponse.getUpdateList();
439                         /*String field_value = changeValueMap.get("field_value");
440                         if(field_value == null)
441                                 field_value = jsonResponse.getFull_table();*/
442                         String field_value = null;
443                         List<String> notifiyList = CachingUtil.getCallbackNotifyList();
444                         if(notifiyList == null || notifiyList.isEmpty()) {
445                                 logger.info("Is cache empty? reconstructing Object from cache..");
446                                 constructJsonCallbackFromCache();
447                                 /*notifiyList = CachingUtil.getCallbackNotifyList();
448                                 if("update".equals(operation)) {
449                                         List<String> updateList = jsonResponse.getUpdateList();
450                                         //logger.info("update list from trigger: "+updateList);
451                                         for(String element : updateList) {
452                                                 logger.info("element: "+element);
453                                                 logger.info("notifiyList: "+notifiyList);
454                                                 if(notifiyList.contains(element)) {
455                                                         logger.info("Found the notifyOn property: "+element);
456                                                         field_value = element;
457                                                 }
458                                         }
459                                 }
460                                 
461                                 baseRequestObj = CachingUtil.getCallBackCache(field_value);
462                                 logger.info("Reconstructing Object from cache is Successful.."+baseRequestObj);*/
463                         }
464                         notifiyList = CachingUtil.getCallbackNotifyList();
465                         JsonCallback baseRequestObj = null;
466                         
467                         if("update".equals(operation)) {
468                                 for(String element: inputUpdateList) {
469                                         baseRequestObj = CachingUtil.getCallBackCache(element);
470                                         if(baseRequestObj != null) {
471                                                 logger.info("Found the element that was changed... "+element);
472                                                 break;
473                                         }
474                                 }
475                                 
476                                 List<String> updateList = jsonResponse.getUpdateList();
477                                 //logger.info("update list from trigger: "+updateList);
478                                 for(String element : updateList) {
479                                         if(notifiyList.contains(element)) {
480                                                 logger.info("Found the notifyOn property: "+element);
481                                                 field_value = element;
482                                                 break;
483                                         }
484                                 }
485                         } else {
486                                 field_value = jsonResponse.getFull_table();
487                                 baseRequestObj = CachingUtil.getCallBackCache(field_value);
488                         }
489                         
490                         if(baseRequestObj == null) {
491                                 resultMap.put("Exception",
492                             "Oops. Something went wrong. Please make sure Callback properties are onboarded.");
493                                 logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.INCORRECTDATA,
494                             ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR);
495                                 return Response.status(Status.BAD_REQUEST).entity(resultMap).build();
496                         }
497                         logger.info("Going through list: "+operation+ " && List: "+jsonResponse.getUpdateList());
498                         
499                         String key = "admin" + "." + "notification_master" + "." + baseRequestObj.getUuid();
500                 String lockId = MusicCore.createLockReference(key);
501                 ReturnType lockAcqResult = MusicCore.acquireLock(key, lockId);
502                 if(! lockAcqResult.getResult().toString().equals("SUCCESS")) {
503                         logger.error(EELFLoggerDelegate.errorLogger, "Some other node is notifying the caller..: ");
504                 }
505                         
506                         logger.info(operation+ ": Operation :: changeValue: "+changeValueMap);
507                         if(operation.equals("update")) {
508                                 String notifyWhenChangeIn = baseRequestObj.getNotifyWhenChangeIn(); // conductor.plans.status
509                                 if(null!=field_value) {
510                                         if(field_value.equals(notifyWhenChangeIn)) {
511                                                 notifyCallBackAppl(jsonResponse, baseRequestObj);
512                                         }
513                                 }
514                         } else if(operation.equals("delete")) {
515                                 String notifyWhenDeletesIn = baseRequestObj.getNotifyWhenDeletesIn(); // conductor.plans.status
516                                 if(null!=field_value) {
517                                         if(field_value.equals(notifyWhenDeletesIn)) {
518                                                 notifyCallBackAppl(jsonResponse, baseRequestObj);
519                                         }
520                                 }
521                         } else if(operation.equals("insert")) {
522                                 String notifyWhenInsertsIn = baseRequestObj.getNotifyWhenInsertsIn(); // conductor.plans.status
523                                 if(null!=field_value) {
524                                         if(field_value.equals(notifyWhenInsertsIn)) {
525                                                 notifyCallBackAppl(jsonResponse, baseRequestObj);
526                                         }
527                                 }
528                         }
529                         MusicCore.releaseLock(lockId, true);    
530             } catch(Exception e) {
531             e.printStackTrace();
532             logger.info("Exception...");
533         }
534         logger.info(EELFLoggerDelegate.applicationLogger, "callback is completed. Notification was sent from Music...");
535         return Response.status(Status.OK).entity(resultMap).build();
536     }
537     
538     private void notifyCallBackAppl(JSONCallbackResponse jsonResponse, JsonCallback baseRequestObj) {
539         int notifytimeout = MusicUtil.getNotifyTimeout();
540         int notifyinterval = MusicUtil.getNotifyInterval();
541         String endpoint = baseRequestObj.getApplicationNotificationEndpoint();
542         String username = baseRequestObj.getApplicationUsername();
543         String password = baseRequestObj.getApplicationPassword();
544         JsonNotification jsonNotification = constructJsonNotification(jsonResponse, baseRequestObj);
545         jsonNotification.setOperation_type(jsonResponse.getOperation());
546         logger.info(EELFLoggerDelegate.applicationLogger, "Notification Response sent is: "+jsonNotification);
547         WebResource webResource = client.resource(endpoint);
548         String authData = username+":"+password;
549         byte[] plainCredsBytes = authData.getBytes();
550         byte[] base64CredsBytes = Base64.encode(plainCredsBytes);
551         String base64Creds = new String(base64CredsBytes);
552         Map<String, String> response_body = baseRequestObj.getResponseBody();
553         ClientResponse response = null;
554         try { 
555                 response = webResource.header("Authorization", "Basic " + base64Creds).accept("application/json").type("application/json")
556             .post(ClientResponse.class, jsonNotification);
557         } catch (com.sun.jersey.api.client.ClientHandlerException chf) {
558                 boolean ok = false;
559                 logger.info(EELFLoggerDelegate.applicationLogger, "Is Service down?");
560                 long now= System.currentTimeMillis();
561                 long end = now+notifytimeout;
562                 while(! ok) {
563                         logger.info(EELFLoggerDelegate.applicationLogger, "retrying since error in notifying callback..");
564                         try {
565                                 response = webResource.header("Authorization", "Basic " + base64Creds).accept("application/json").type("application/json")
566                             .post(ClientResponse.class, jsonNotification);
567                                 if(response.getStatus() == 200) ok = true;
568                         }catch (Exception e) {
569                                 logger.info(EELFLoggerDelegate.applicationLogger, "Retry until "+(end-System.currentTimeMillis()));
570                                 if(response == null && System.currentTimeMillis() < end) ok = false;
571                                 else ok = true;
572                                 try{ Thread.sleep(notifyinterval); } catch(Exception e1) {}
573                         }
574                 }
575         }
576         if(response == null) {
577                 logger.error(EELFLoggerDelegate.errorLogger, "Can NOT notify the caller as caller failed to respond..");
578                 return;
579         }
580         JsonNotifyClientResponse responseStr = response.getEntity(JsonNotifyClientResponse.class);
581         logger.info(EELFLoggerDelegate.applicationLogger, "Response from Notified client: "+responseStr);
582         
583         if(response.getStatus() != 200){
584                 long now= System.currentTimeMillis();
585                 long end = now+30000;
586                 while(response.getStatus() != 200 && System.currentTimeMillis() < end) {
587                         logger.info(EELFLoggerDelegate.applicationLogger, "retrying since error in notifying callback..");
588                         response = webResource.header("Authorization", "Basic " + base64Creds).accept("application/json").type("application/json")
589                             .post(ClientResponse.class, jsonNotification);
590                 }
591                 logger.info(EELFLoggerDelegate.applicationLogger, "Exception while notifying.. "+response.getStatus());
592         }
593     }
594     
595     private JsonNotification constructJsonNotification(JSONCallbackResponse jsonResponse, JsonCallback baseRequestObj) {
596         
597         JsonNotification jsonNotification = new JsonNotification();
598         try {
599                 jsonNotification.setNotify_field(baseRequestObj.getNotifyOn());
600                 jsonNotification.setEndpoint(baseRequestObj.getApplicationNotificationEndpoint());
601                 jsonNotification.setUsername(baseRequestObj.getApplicationUsername());
602                 jsonNotification.setPassword(baseRequestObj.getApplicationPassword());
603                 String pkValue = jsonResponse.getPrimary_key();
604                 
605                 String[] fullNotifyArr = baseRequestObj.getNotifyOn().split(":");
606                 
607                 String[] tableArr = fullNotifyArr[0].split("\\.");
608                 TableMetadata tableInfo = MusicCore.returnColumnMetadata(tableArr[0], tableArr[1]);
609                         DataType primaryIdType = tableInfo.getPrimaryKey().get(0).getType();
610                         String primaryId = tableInfo.getPrimaryKey().get(0).getName();
611                         
612                         Map<String, String> responseBodyMap = baseRequestObj.getResponseBody();
613                         for (Entry<String, String> entry : new HashSet<>(responseBodyMap.entrySet())) {
614                             String trimmed = entry.getKey().trim();
615                             if (!trimmed.equals(entry.getKey())) {
616                                 responseBodyMap.remove(entry.getKey());
617                                 responseBodyMap.put(trimmed, entry.getValue());
618                             }
619                         }
620                         
621                 Set<String> keySet = responseBodyMap.keySet();
622                 String cql = "select *";
623                 /*for(String keys: keySet) {
624                         cql = cql + keys + ",";
625                 }*/
626                 //cql = cql.substring(0, cql.length()-1);
627                 cql = cql + " FROM "+fullNotifyArr[0]+" WHERE "+primaryId+" = ?";
628                 logger.info("CQL in constructJsonNotification: "+cql);
629                 PreparedQueryObject pQuery = new PreparedQueryObject();
630                 pQuery.appendQueryString(cql);
631                 pQuery.addValue(MusicUtil.convertToActualDataType(primaryIdType, pkValue));
632                 Row row = MusicCore.get(pQuery).one();
633                 Map<String, String> newMap = new HashMap<>();
634                 if(row != null) {
635                         ColumnDefinitions colInfo = row.getColumnDefinitions();
636                     for (Definition definition : colInfo) {
637                         String colName = definition.getName();
638                         if(keySet.contains(colName)) {
639                                 DataType colType = definition.getType();
640                                 Object valueObj = MusicCore.getDSHandle().getColValue(row, colName, colType);
641                                 Object valueString = MusicUtil.convertToActualDataType(colType, valueObj);
642                                 logger.info(colName+" : "+valueString);
643                                 newMap.put(colName, valueString.toString());
644                                 keySet.remove(colName);
645                         }
646                     }
647                 }
648                 if(! keySet.isEmpty()) {
649                         Iterator<String> iterator = keySet.iterator();
650                         while (iterator.hasNext()) {
651                             String element = iterator.next();
652                             newMap.put(element,"COLUMN_NOT_FOUND");
653                         }
654                 }
655             
656                         /*if(row != null) {
657                                 for(String keys: keySet1) {
658                                         String value = null;
659                                         try {
660                                                 logger.info(">>>>>>>> converting <<<<<<<<<<<< "+keys + " : "+responseBodyMap.get(keys));
661                                                 if(responseBodyMap.get(keys).equals("uuid"))
662                                                         value = row.getUUID(keys.trim()).toString();
663                                                 else if (responseBodyMap.get(keys).equals("text"))
664                                                         value = row.getString(keys.trim());
665                                                 else if (responseBodyMap.get(keys).equals("int"))
666                                                         value = String.valueOf(row.getInt(keys.trim()));
667                                                 else if (responseBodyMap.get(keys).equals("bigint"))
668                                                         value = String.valueOf(row.getLong(keys.trim()));
669                                         } catch (Exception e) {
670                                         newMap.put(primaryId, pkValue);
671                                         logger.info("Error in constructJsonNotification: Invalid column..");
672                                 }
673                                         else if (responseBodyMap.get(keys).contains("int"))
674                                                 value = row.getLong(keys).toString();
675                                         newMap.put(keys.trim(), value);
676                                 }
677                         }*/
678                 
679                 if("delete".equals(jsonResponse.getOperation()) || newMap.isEmpty()) {
680                         newMap.put(primaryId, pkValue);
681                 }
682                 jsonNotification.setResponse_body(newMap);
683         } catch(Exception e) {
684                 e.printStackTrace();
685         }
686         return jsonNotification;
687     }
688     
689     private void constructJsonCallbackFromCache() throws Exception{
690         PreparedQueryObject pQuery = new PreparedQueryObject();
691                 JsonCallback jsonCallback = null;
692                 List<String> notifyList = new java.util.ArrayList<>();
693                 String cql = 
694                 "select id, endpoint_userid, endpoint_password, notify_to_endpoint, notify_insert_on,"
695                 + " notify_delete_on, notify_update_on, request, notifyon from admin.notification_master allow filtering";
696         pQuery.appendQueryString(cql);
697         //pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), fullTable));
698         logger.info("Query: "+pQuery.getQuery());
699         
700         ResultSet rs = MusicCore.get(pQuery);
701         Iterator<Row> it = rs.iterator();
702         while (it.hasNext()) {
703             Row row = (Row) it.next();
704                 String endpoint = row.getString("notify_to_endpoint");
705             String username = row.getString("endpoint_userid");
706             ByteBuffer passwordBytes = row.getBytes("endpoint_password");
707             String insert = row.getString("notify_insert_on");
708             String delete = row.getString("notify_delete_on");
709             String update = row.getString("notify_update_on");
710             String request = row.getString("request");
711             String notifyon = row.getString("notifyon");
712             String uuid = row.getUUID("id").toString();
713             notifyList.add(notifyon);
714             jsonCallback = new JsonCallback();
715             jsonCallback.setApplicationNotificationEndpoint(endpoint);
716             
717             Charset charset = Charset.forName("ISO-8859-1");
718             String decodedPwd = charset.decode(passwordBytes).toString();
719             jsonCallback.setApplicationPassword(decodedPwd);
720             jsonCallback.setApplicationUsername(username);
721             jsonCallback.setNotifyOn(notifyon);
722             jsonCallback.setNotifyWhenInsertsIn(insert);
723             jsonCallback.setNotifyWhenDeletesIn(delete);
724             jsonCallback.setNotifyWhenChangeIn(update);
725             jsonCallback.setUuid(uuid);
726             logger.info("From DB. Saved request_body: "+request);
727             request = request.substring(1, request.length()-1);           
728             String[] keyValuePairs = request.split(",");              
729             Map<String,String> responseBody = new HashMap<>();               
730
731             for(String pair : keyValuePairs) {
732                 String[] entry = pair.split("="); 
733                 String val = "";
734                 if(entry.length == 2)
735                         val = entry[1];
736                 responseBody.put(entry[0], val);          
737             }
738             logger.info("After parsing. Saved request_body: "+responseBody);
739             jsonCallback.setResponseBody(responseBody);
740             logger.info("Updating Cache with updateCallBackCache: "+notifyon+ " :::: "+jsonCallback);
741             CachingUtil.updateCallBackCache(notifyon, jsonCallback);
742         }
743         CachingUtil.updateCallbackNotifyList(notifyList);
744     }
745     
746     @POST
747     @Path("/onboardCallback")
748     @Produces(MediaType.APPLICATION_JSON)
749     @Consumes(MediaType.APPLICATION_JSON)
750     public Response addCallback(JsonNotification jsonNotification) {
751         Map<String, Object> resultMap = new HashMap<>();
752         ResponseBuilder response =
753                         Response.noContent().header("X-latestVersion", MusicUtil.getVersion());
754         String username = jsonNotification.getUsername();
755         String password = jsonNotification.getPassword();
756         String endpoint = jsonNotification.getEndpoint();
757         String notify_field = jsonNotification.getNotify_field();
758         Map<String, String> responseBody = jsonNotification.getResponse_body();
759         String triggerName = jsonNotification.getTriggerName();
760         if(triggerName == null || triggerName.length() == 0)
761                 triggerName = "MusicTrigger";
762         
763         /*JsonCallback callBackCache = CachingUtil.getCallBackCache(notify_field);
764         if(callBackCache != null) {
765                 resultMap.put("Exception", "The notification property has already been onboarded.");
766             return Response.status(Status.BAD_REQUEST).entity(resultMap).build();
767         }*/
768         
769         String[] allFields = notify_field.split(":");
770         String inserts = null;
771         String updates = null;
772         String deletes = null;
773         String tableName = null;
774         if(allFields.length >= 2) {
775                 inserts = updates = notify_field;
776         } else if(allFields.length == 1) {
777                 inserts = deletes = notify_field;;
778         }
779         tableName = allFields[0];
780         String cql = "CREATE TRIGGER IF NOT EXISTS musictrigger ON "+tableName+" Using '"+triggerName+"'";
781         PreparedQueryObject pQuery = new PreparedQueryObject();
782         
783         String uuid = CachingUtil.generateUUID();
784         try {
785                 pQuery.appendQueryString(
786                                 "INSERT INTO admin.notification_master (id, endpoint_userid, endpoint_password, notify_to_endpoint, "
787                                                 + "notifyon, notify_insert_on, notify_delete_on, notify_update_on, request, current_notifier) VALUES (?,?,?,?,?,?,?,?,?,?)");
788                 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), uuid));
789                 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), username));
790                 Charset charset = Charset.forName("ISO-8859-1");
791             ByteBuffer decodedPwd = charset.encode(password); 
792                 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.blob(), decodedPwd.array()));
793                 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), endpoint));
794                 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), notify_field));
795                 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), inserts));
796                 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), deletes));
797                 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), updates));
798                 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), responseBody));
799                 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), MusicCore.getMyHostId()));
800                 MusicCore.nonKeyRelatedPut(pQuery, MusicUtil.EVENTUAL);
801                 JsonCallback jsonCallback = new JsonCallback();
802                 jsonCallback.setUuid(uuid);
803                 jsonCallback.setApplicationNotificationEndpoint(endpoint);
804                 jsonCallback.setApplicationPassword(password);
805                 jsonCallback.setApplicationUsername(username);
806                 jsonCallback.setNotifyOn(notify_field);
807                 jsonCallback.setNotifyWhenChangeIn(updates);
808                 jsonCallback.setNotifyWhenDeletesIn(deletes);
809                 jsonCallback.setNotifyWhenInsertsIn(inserts);
810                 jsonCallback.setResponseBody(responseBody);
811                 CachingUtil.updateCallBackCache(notify_field, jsonCallback);
812                 logger.info("Cache updated ");
813                 pQuery = new PreparedQueryObject();
814                 pQuery.appendQueryString(cql);
815                 ResultType nonKeyRelatedPut = MusicCore.nonKeyRelatedPut(pQuery, MusicUtil.EVENTUAL);
816                 logger.info(EELFLoggerDelegate.applicationLogger, "Created trigger");
817         //callBackCache.put(jsonCallback.getApplicationName(), jsonMap);
818         } catch (InvalidQueryException e) {
819             logger.error(EELFLoggerDelegate.errorLogger,"Exception callback_api table not configured."+e.getMessage());
820             resultMap.put("Exception", "Please make sure admin.notification_master table is configured.");
821             return Response.status(Status.BAD_REQUEST).entity(resultMap).build();
822         } catch(Exception e) {
823                 e.printStackTrace();
824                 resultMap.put("Exception", "Exception Occured.");
825             return Response.status(Status.BAD_REQUEST).entity(resultMap).build();
826         }
827        return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setMessage("Callback api successfully registered").toMap()).build();
828     }
829
830     /*public String encodePwd(String password) {
831         return Base64.getEncoder().encodeToString(password.getBytes());
832     }
833     
834     public String decodePwd(String password) {
835         byte[] bytes = Base64.getDecoder().decode(password); 
836         return new String(bytes);
837     }*/
838 }