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.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;
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;
96 import io.swagger.annotations.Api;
97 import io.swagger.annotations.ApiOperation;
100 // @Path("/v{version: [0-9]+}/admin")
102 @Api(value = "Admin Api", hidden = true)
103 public class RestMusicAdminAPI {
104 private static EELFLoggerDelegate logger =
105 EELFLoggerDelegate.getLogger(RestMusicAdminAPI.class);
107 * API to onboard an application with MUSIC. This is the mandatory first step.
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();
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();
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));
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();
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();
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();
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()) {
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 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();
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();
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);
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");
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();
278 return response.status(Status.OK).entity(resultMap).build();
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();
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);
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();
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();
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;
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();
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();
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();
371 pQuery = new PreparedQueryObject();
372 StringBuilder preCql = new StringBuilder("UPDATE admin.keyspace_master SET ");
374 preCql.append(" application_name = ?,");
376 preCql.append(" username = ?,");
377 if (password != null)
378 preCql.append(" password = ?,");
380 preCql.append(" is_aaf = ?,");
381 preCql.deleteCharAt(preCql.length() - 1);
382 preCql.append(" WHERE uuid = ? IF EXISTS");
383 pQuery.appendQueryString(preCql.toString());
385 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName));
387 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), userId));
388 if (password != null)
389 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), BCrypt.hashpw(password, BCrypt.gensalt())));
391 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), isAAF));
393 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), UUID.fromString(aid)));
394 ResultType result = MusicCore.nonKeyRelatedPut(pQuery, consistency);
396 if (result == ResultType.SUCCESS) {
397 resultMap.put("Success", "Your application has been updated successfully");
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();
406 return response.status(Status.OK).entity(resultMap).build();
409 Client client = Client.create();
410 ObjectMapper mapper = new ObjectMapper();
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() {
421 makeAsyncCall(inputJsonObj);
425 return response.status(Status.OK).entity(resultMap).build();
428 private Response makeAsyncCall(JSONObject inputJsonObj) {
429 ResponseBuilder response = Response.noContent().header("X-latestVersion", MusicUtil.getVersion());
430 Map<String, Object> resultMap = new HashMap<>();
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();
443 List<String> inputUpdateList = jsonResponse.getUpdateList();
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();
451 notifiyList = CachingUtil.getCallbackNotifyList();
452 JsonCallback baseRequestObj = null;
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);
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;
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];
478 baseRequestObj = CachingUtil.getCallBackCache(newElement);
479 if(baseRequestObj != null) {
480 logger.info("Found the element that was changed... "+newElement);
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];
490 if(notifiyList.contains(newElement)) {
491 logger.info("Found the notifyOn property: "+newElement);
492 field_value = newElement;
498 field_value = jsonResponse.getFull_table();
499 baseRequestObj = CachingUtil.getCallBackCache(field_value);
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();
509 logger.info(EELFLoggerDelegate.applicationLogger, "Going through list: "+operation+ " && List: "+jsonResponse.getUpdateList());
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..: ");
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);
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);
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);
535 MusicCore.releaseLock(lockId, true);
536 } catch(Exception e) {
538 logger.error(EELFLoggerDelegate.errorLogger, "Exception while notifying...."+e.getMessage());
540 logger.info(EELFLoggerDelegate.applicationLogger, "callback is completed. Notification was sent from Music...");
541 return response.status(Status.OK).entity(resultMap).build();
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;
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){}
574 // Install the all-trusting trust manager
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);
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() {
590 public boolean verify( String s, SSLSession sslSession ) {
595 properties.put( HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, httpsProperties );
596 HttpsURLConnection.setDefaultHostnameVerifier (new HostnameVerifier() {
598 public boolean verify(String hostname, SSLSession session) {
602 Client.create( config );
604 catch ( KeyManagementException | NoSuchAlgorithmException e ) {
605 throw new RuntimeException( e );
608 service = client.resource(endpoint);
610 response = service.header("Authorization", "Basic " + base64Creds).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON)
611 .post(ClientResponse.class, jsonNotification);
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);
617 if(response != null && response.getStatus() == 200) ok = true;
619 long now= System.currentTimeMillis();
620 long end = now+notifytimeout;
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()));
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) {}
633 }catch (Exception e) {
634 logger.info(EELFLoggerDelegate.applicationLogger, "Retry until "+(end-System.currentTimeMillis()));
635 if(response == null && System.currentTimeMillis() < end) ok = false;
637 try{ Thread.sleep(notifyinterval); } catch(Exception e1) {}
642 if(response == null) {
643 logger.error(EELFLoggerDelegate.errorLogger, "Can NOT notify the caller as caller failed to respond..");
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..");
657 private JsonNotification constructJsonNotification(JSONCallbackResponse jsonResponse, JsonCallback baseRequestObj) {
659 JsonNotification jsonNotification = new JsonNotification();
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();
667 String[] fullNotifyArr = baseRequestObj.getNotifyOn().split(":");
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();
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());
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;
689 logger.info(EELFLoggerDelegate.applicationLogger, "responseBodyMap is not blank: "+responseBodyMap);
690 String cql = "select *";
691 /*for(String keys: keySet) {
692 cql = cql + keys + ",";
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();
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);
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");
723 if("delete".equals(jsonResponse.getOperation()) || newMap.isEmpty()) {
724 newMap.put(primaryId, pkValue);
726 jsonNotification.setResponse_body(newMap);
727 } catch(Exception e) {
728 logger.error(e.getMessage(), e);
731 return jsonNotification;
736 private void constructJsonCallbackFromCache() throws Exception{
737 PreparedQueryObject pQuery = new PreparedQueryObject();
738 JsonCallback jsonCallback = null;
739 List<String> notifyList = new java.util.ArrayList<>();
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);
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);
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<>();
776 for(String pair : keyValuePairs) {
777 String[] entry = pair.split("=");
779 if(entry.length == 2)
781 responseBody.put(entry[0], val);
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);
788 CachingUtil.updateCallbackNotifyList(notifyList);
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";
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;;
818 tableName = allFields[0];
819 String cql = "CREATE TRIGGER IF NOT EXISTS musictrigger ON "+tableName+" Using '"+triggerName+"'";
820 PreparedQueryObject pQuery = new PreparedQueryObject();
822 String uuid = CachingUtil.generateUUID();
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);
862 resultMap.put("Exception", "Exception Occured.");
863 return response.status(Status.BAD_REQUEST).entity(resultMap).build();
865 return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setMessage("Callback api successfully registered").toMap()).build();
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();
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();
885 return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setMessage("Callback api successfully deleted").toMap()).build();