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