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