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