2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
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.
18 * ============LICENSE_END=========================================================
21 package org.openecomp.policy.utils;
23 import java.io.IOException;
24 import java.util.Date;
25 import java.util.List;
26 import java.util.Properties;
27 import java.util.concurrent.TimeUnit;
29 import javax.persistence.EntityManager;
30 import javax.persistence.EntityManagerFactory;
31 import javax.persistence.EntityTransaction;
32 import javax.persistence.Persistence;
33 import javax.persistence.Query;
35 import org.apache.log4j.Logger;
36 import org.eclipse.persistence.config.PersistenceUnitProperties;
37 import org.openecomp.policy.api.PDPNotification;
38 import org.openecomp.policy.jpa.BackUpMonitorEntity;
39 import org.openecomp.policy.std.NotificationStore;
40 import org.openecomp.policy.std.StdPDPNotification;
42 import com.fasterxml.jackson.databind.JsonNode;
43 import com.github.fge.jackson.JsonLoader;
44 import com.github.fge.jsonpatch.JsonPatch;
45 import com.github.fge.jsonpatch.JsonPatchException;
46 import com.github.fge.jsonpatch.diff.JsonDiff;
49 * BackUp Monitor checks Backup Status with the Database and maintains Redundancy for Gateway Applications.
52 public class BackUpMonitor {
53 private static final Logger logger = Logger.getLogger(BackUpMonitor.class.getName());
54 private static final int DEFAULT_PING = 60000; // Value is in milliseconds.
56 private static BackUpMonitor instance = null;
57 private static String resourceName = null;
58 private static String resourceNodeName = null;
59 private static String notificationRecord = null;
60 private static String lastMasterNotification= null;
61 private static int pingInterval = DEFAULT_PING;
62 private static Boolean masterFlag = false;
63 private static Object lock = new Object();
64 private static Object notificationLock = new Object();
65 private static BackUpHandler handler= null;
66 private EntityManager em;
67 private EntityManagerFactory emf;
70 * Enumeration for the Resource Node Naming. Add here if required.
72 public enum ResourceNode{
77 private BackUpMonitor(String resourceNodeName, String resourceName, Properties properties, BackUpHandler handler) throws Exception{
81 BackUpMonitor.resourceNodeName = resourceNodeName;
82 BackUpMonitor.resourceName = resourceName;
83 BackUpMonitor.handler = handler;
84 // Create Persistence Entity
85 properties.setProperty(PersistenceUnitProperties.ECLIPSELINK_PERSISTENCE_XML, "META-INF/persistencePU.xml");
86 emf = Persistence.createEntityManagerFactory("PolicyEngineUtils", properties);
88 logger.error("Unable to create Entity Manger Factory ");
89 throw new Exception("Unable to create Entity Manger Factory");
91 em = emf.createEntityManager();
93 // Check Database if this is Master or Slave.
97 Thread t = new Thread(new BMonitor());
102 * Gets the BackUpMonitor Instance if given proper resourceName and properties. Else returns null.
104 * @param resourceNodeName String format of the Resource Node to which the resource Belongs to.
105 * @param resourceName String format of the ResourceName. This needs to be Unique.
106 * @param properties Properties format of the properties file.
107 * @return BackUpMonitor instance.
109 public static synchronized BackUpMonitor getInstance(String resourceNodeName, String resourceName, Properties properties, BackUpHandler handler) throws Exception {
110 if(resourceNodeName==null || resourceNodeName.trim().equals("") ||resourceName==null|| resourceName.trim().equals("") || properties == null || handler==null){
111 logger.error("Error while getting Instance. Please check resourceName and/or properties file");
113 }else if((resourceNodeName.equals(ResourceNode.ASTRA.toString()) || resourceNodeName.equals(ResourceNode.BRMS.toString())) && validate(properties) && instance==null){
114 logger.info("Creating Instance of BackUpMonitor");
115 instance = new BackUpMonitor(resourceNodeName, resourceName, properties, handler);
120 // This is to validate given Properties with required values.
121 private static Boolean validate(Properties properties){
122 if(properties.getProperty("javax.persistence.jdbc.driver")==null ||properties.getProperty("javax.persistence.jdbc.driver").trim().equals("")){
123 logger.error("javax.persistence.jdbc.driver property is empty");
126 if(properties.getProperty("javax.persistence.jdbc.url")==null || properties.getProperty("javax.persistence.jdbc.url").trim().equals("")){
127 logger.error("javax.persistence.jdbc.url property is empty");
130 if(properties.getProperty("javax.persistence.jdbc.user")==null || properties.getProperty("javax.persistence.jdbc.user").trim().equals("")){
131 logger.error("javax.persistence.jdbc.user property is empty");
134 if(properties.getProperty("javax.persistence.jdbc.password")==null || properties.getProperty("javax.persistence.jdbc.password").trim().equals("")){
135 logger.error("javax.persistence.jdbc.password property is empty");
138 if(properties.getProperty("ping_interval")==null || properties.getProperty("ping_interval").trim().equals("")){
139 logger.info("ping_interval property not specified. Taking default value");
142 pingInterval = Integer.parseInt(properties.getProperty("ping_interval").trim());
143 }catch(NumberFormatException e){
144 logger.warn("Ignored invalid proeprty ping_interval. Taking default value.");
145 pingInterval = DEFAULT_PING;
151 // Sets the Flag for masterFlag to either True or False.
152 private static void setFlag(Boolean flag){
153 synchronized (lock) {
159 * Gets the Boolean value of Master(True) or Slave mode (False)
161 * @return Boolean flag which if True means that the operation needs to be performed(Master mode) or if false the operation is in slave mode.
163 public synchronized Boolean getFlag(){
164 synchronized (lock) {
169 // BackUpMonitor Thread
170 private class BMonitor implements Runnable{
173 logger.info("Starting BackUpMonitor Thread.. ");
176 TimeUnit.MILLISECONDS.sleep(pingInterval);
178 } catch (Exception e) {
179 logger.error("Error during Thread execution " + e.getMessage());
186 private static BackUpMonitorEntity setMaster(BackUpMonitorEntity bMEntity){
187 bMEntity.setFlag("MASTER");
193 private static BackUpMonitorEntity setSlave(BackUpMonitorEntity bMEntity){
194 bMEntity.setFlag("SLAVE");
199 // Check Database and set the Flag.
200 private void checkDataBase() throws Exception {
201 EntityTransaction et = em.getTransaction();
202 notificationRecord = PolicyUtils.objectToJsonString(NotificationStore.getNotificationRecord());
204 logger.info("Clearing Cache");
205 em.getEntityManagerFactory().getCache().evictAll();
207 logger.info("Checking Datatbase for BackUpMonitor.. ");
209 Query query = em.createQuery("select b from BackUpMonitorEntity b where b.resourceNodeName = :nn");
210 if(resourceNodeName.equals(ResourceNode.ASTRA.toString())){
211 query.setParameter("nn", ResourceNode.ASTRA.toString());
212 }else if(resourceNodeName.equals(ResourceNode.BRMS.toString())){
213 query.setParameter("nn", ResourceNode.BRMS.toString());
215 List<?> bMList = query.getResultList();
216 if(bMList.isEmpty()){
217 // This is New. create an entry as Master.
218 logger.info("Adding resource " + resourceName + " to Database");
219 BackUpMonitorEntity bMEntity = new BackUpMonitorEntity();
220 bMEntity.setResoruceNodeName(resourceNodeName);
221 bMEntity.setResourceName(resourceName);
222 bMEntity = setMaster(bMEntity);
223 bMEntity.setTimeStamp(new Date());
224 em.persist(bMEntity);
227 // Check if resourceName already exists and if there is a Master in Node.
228 Boolean masterFlag = false;
229 Boolean alreadyMaster = false;
231 BackUpMonitorEntity masterEntity = null;
232 BackUpMonitorEntity slaveEntity = null;
234 for(int i=0; i< bMList.size(); i++){
235 BackUpMonitorEntity bMEntity = (BackUpMonitorEntity) bMList.get(i);
236 logger.info("Refreshing Entity. ");
237 em.refresh(bMEntity);
238 if(bMEntity.getResourceName().equals(resourceName)){
239 logger.info("Resource Name already Exists. " + resourceName);
240 if(bMEntity.getFlag().equalsIgnoreCase("MASTER")){
243 logger.info(resourceName + " is on Master Mode");
244 bMEntity.setTimeStamp(new Date());
245 bMEntity.setNotificationRecord(notificationRecord);
246 em.persist(bMEntity);
248 setLastNotification(null);
249 alreadyMaster = true;
254 slaveEntity = bMEntity;
255 logger.info(resourceName + " is on Slave Mode");
258 if(bMEntity.getFlag().equalsIgnoreCase("MASTER")){
259 // check if its time stamp is old.
260 currentTime = new Date();
261 timeDiff = currentTime.getTime()-bMEntity.getTimeStamp().getTime();
262 masterEntity = bMEntity;
267 // If there is master and no slave entry then add the slave entry to database.
268 // If we are slave and there is a masterFlag then check timeStamp to find if master is down.
270 BackUpMonitorEntity bMEntity;
271 if(slaveEntity==null){
272 bMEntity = new BackUpMonitorEntity();
274 bMEntity = slaveEntity;
276 if(masterFlag && !getFlag()){
277 if(timeDiff > pingInterval){
278 // This is down or has an issue and we need to become Master while turning the Master to slave.
279 masterEntity = setSlave(masterEntity);
280 String lastNotification = null;
281 if(masterEntity.getNotificationRecord()!=null){
282 lastNotification = calculatePatch(masterEntity.getNotificationRecord());
284 setLastNotification(lastNotification);
285 em.persist(masterEntity);
287 bMEntity = setMaster(bMEntity);
288 logger.info(resourceName + " changed to Master Mode");
290 bMEntity = setSlave(bMEntity);
291 setLastNotification(null);
292 logger.info(resourceName + " is on Slave Mode");
295 // If there is no Master. we need to become Master.
296 bMEntity = setMaster(bMEntity);
297 logger.info(resourceName + " is on Master Mode");
298 setLastNotification(null);
300 bMEntity.setNotificationRecord(notificationRecord);
301 bMEntity.setResoruceNodeName(resourceNodeName);
302 bMEntity.setResourceName(resourceName);
303 bMEntity.setTimeStamp(new Date());
304 em.persist(bMEntity);
310 logger.error("failed Database Operation " + e.getMessage());
314 throw new Exception(e);
318 // Calculate Patch and return String JsonPatch of the notification Delta.
319 private synchronized String calculatePatch(String oldNotificationRecord) {
321 JsonNode notification = JsonLoader.fromString(notificationRecord);
322 JsonNode oldNotification = JsonLoader.fromString(oldNotificationRecord);
323 JsonNode patchNode = JsonDiff.asJson(oldNotification, notification);
324 logger.info("Generated JSON Patch is " + patchNode.toString());
325 JsonPatch patch = JsonPatch.fromJson(patchNode);
327 JsonNode patched = patch.apply(oldNotification);
328 logger.info("Generated New Notification is : " + patched.toString());
329 return patched.toString();
330 } catch (JsonPatchException e) {
331 logger.error("Error generating Patched " +e.getMessage());
334 }catch(IOException e){
335 logger.error("Error generating Patched " +e.getMessage());
341 * Updates Notification in the Database while Performing the health check.
343 * @param notification String format of notification record to store in the Database.
346 public synchronized void updateNotification() throws Exception{
350 // Take in string notification and send the record delta to Handler.
351 private static void callHandler(String notification){
354 PDPNotification notificationObject = PolicyUtils.jsonStringToObject(notification, StdPDPNotification.class);
355 if(notificationObject.getNotificationType()!=null){
356 logger.info("Performing Patched notification ");
358 handler.runOnNotification(notificationObject);
359 }catch (Exception e){
360 logger.error("Error in Clients Handler Object : " + e.getMessage());
363 } catch (IOException e) {
364 logger.info("Error while notification Conversion " + e.getMessage());
369 // Used to set LastMasterNotification Record.
370 private static void setLastNotification(String notification){
371 synchronized(notificationLock){
372 lastMasterNotification = notification;
373 if(lastMasterNotification!=null && !lastMasterNotification.equals("\"notificationType\":null")){
374 callHandler(notification);