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