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