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