ClusterRefresher.java

package sk.iway.iwcm.system.cluster;

import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

import org.eclipse.persistence.jpa.JpaEntityManager;

import sk.iway.iwcm.Adminlog;
import sk.iway.iwcm.Cache;
import sk.iway.iwcm.Constants;
import sk.iway.iwcm.DB;
import sk.iway.iwcm.Logger;
import sk.iway.iwcm.Tools;
import sk.iway.iwcm.database.SimpleQuery;
import sk.iway.iwcm.doc.DocDB;
import sk.iway.iwcm.system.ConfDB;
import sk.iway.iwcm.system.jpa.JpaTools;
import sk.iway.iwcm.tags.CombineTag;


/**
 *  ClusterRefresher.java
 *  objekt pravidelne kontrolujuci databazu clustra pre obnovu dat
 *
 *@Title        webjet4
 *@Company      Interway s.r.o. (www.interway.sk)
 *@Copyright    Interway s.r.o. (c) 2001-2008
 *@author       $Author: jeeff $
 *@version      $Revision: 1.5 $
 *@created      Date: 17.11.2008 16:50:16
 *@modified     $Date: 2010/01/11 08:15:01 $
 */
public class ClusterRefresher extends TimerTask
{
	private Timer timer;
	public static final String THREAD_NAME = "ClusterRefresherThread";

	//pouzivane v rezime auto na identifikaciu poslednej hodnoty z DB tabulky ktoru mame precitanu
	private static int lastExecutedAutoId = -1;

	/**
	 * Konstruktor, ak je nastavene clusterNames tak inicializuje Timer task
	 */
	public ClusterRefresher()
	{
		String clusterNames = Constants.getString("clusterNames");
		if (Tools.isEmpty(clusterNames)) return;
		String clusterMyNodeName = Constants.getString("clusterMyNodeName");

		if ("auto".equals(Constants.getString("clusterNames")))
		{
			//inicializuj aktualne poslednu hodnotu z DB tabulky pre auto aktualizaciu nodov
			setLastExecutedAutoId(new SimpleQuery().forInt("SELECT MAX(cluster_refresh_id) FROM cluster_refresher WHERE (node_name=? OR node_name=?) ", "auto", clusterMyNodeName));
		}

		timer = new Timer(true);
		timer.schedule(this, 5000, Constants.getInt("clusterRefreshTimeout"));
	}

	/**
	 * Kontrola aktualizacii objektov
	 */
	@Override
	public void run()
	{
		try
		{
			String clusterMyNodeName = Constants.getString("clusterMyNodeName");
			if (Tools.isEmpty(clusterMyNodeName)) return;

			if ("auto".equals(Constants.getString("clusterNames")))
			{
				readFromAutoMode(clusterMyNodeName);
			}
			else
			{
				readFromDB(clusterMyNodeName);
			}

			try
			{
				JpaEntityManager em = JpaTools.getEclipseLinkEntityManager();
				if(em != null)
				{
					em.getEntityManagerFactory().getCache().evictAll();
				}
			}
			catch (Exception ex)
			{
				Adminlog.add(Adminlog.TYPE_CRON, "ClusterRefresher.run ERROR JPA " + ex.getMessage()+"\n\n"+Logger.getStackTrace(ex), -1, -1);
				sk.iway.iwcm.Logger.error(ex);
			}

			//zmaz stare zaznamy nebeziacich nodov
			cleanOldStatusAllNodes();
		}
		catch (Exception ex)
		{
			Adminlog.add(Adminlog.TYPE_CRON, "ClusterRefresher.run ERROR " + ex.getMessage()+"\n\n"+Logger.getStackTrace(ex), -1, -1);
			Logger.error(ClusterRefresher.class, ex);
		}
	}

	/**
	 * Precitanie zaznamov z DB pre moj node (ak mame presne zadane mena nodov)
	 * @param clusterMyNodeName
	 */
	private void readFromDB(String clusterMyNodeName)
	{
		try
		{
			List<String> updateClassNames = new SimpleQuery().forListString("SELECT class_name FROM cluster_refresher WHERE node_name=? ORDER BY cluster_refresh_id ASC", clusterMyNodeName);

			Thread.currentThread().setName(THREAD_NAME);
			Set<String> allreadyExecuted = new HashSet<>();
			for (String className : updateClassNames)
			{
				if (allreadyExecuted.contains(className)) continue;

				refreshObject(clusterMyNodeName, className);
				allreadyExecuted.add(className);
			}
		}
		catch (IllegalStateException ex)
		{
			Adminlog.add(Adminlog.TYPE_CRON, "ClusterRefresher.run ERROR 1 " + ex.getMessage()+"\n\n"+Logger.getStackTrace(ex), -1, -1);
			Logger.error(ClusterRefresher.class, ex);
		}
		catch (Exception ex)
		{
			Adminlog.add(Adminlog.TYPE_CRON, "ClusterRefresher.run ERROR 2 " + ex.getMessage()+"\n\n"+Logger.getStackTrace(ex), -1, -1);
			Logger.error(ClusterRefresher.class, ex);
		}
	}

	/**
	 * Precitanie zaznamov z DB ak mame rezim auto, cize kazdy nod si sam pamata co ma aktualizovat (cita len nove zaznamy z DB)
	 * @param clusterMyNodeName
	 */
	private void readFromAutoMode(String clusterMyNodeName)
	{
		try
		{
			//we need myNodeName to read monitoring refresher data
			int actualMax = new SimpleQuery().forInt("SELECT MAX(cluster_refresh_id) FROM cluster_refresher WHERE (node_name=? OR node_name=?) AND cluster_refresh_id>?", "auto", clusterMyNodeName, getLastExecutedAutoId());
			//getLastExecutedAutoId()>0 - ak je to prvy beh, tak nechceme robit nic, su tam zapisane data z tohto nodu
			if (actualMax > 0 && getLastExecutedAutoId()>=0)
			{
				List<String> updateClassNames = new SimpleQuery().forListString("SELECT class_name FROM cluster_refresher WHERE (node_name=? OR node_name=?)  AND cluster_refresh_id>? AND cluster_refresh_id<=? ORDER BY cluster_refresh_id ASC", "auto", clusterMyNodeName, getLastExecutedAutoId(), actualMax);

				Thread.currentThread().setName(THREAD_NAME);
				Set<String> allreadyExecuted = new HashSet<>();
				for (String className : updateClassNames)
				{
					if (allreadyExecuted.contains(className)) continue;

					refreshObject(clusterMyNodeName, className);
					allreadyExecuted.add(className);
				}

				Logger.debug(ClusterRefresher.class, "readFromAutoMode, actualMax="+actualMax+" lastExecutedAutoId="+getLastExecutedAutoId());
			}

			if (actualMax > 0) setLastExecutedAutoId(actualMax);
		}
		catch (Exception ex)
		{
			Adminlog.add(Adminlog.TYPE_CRON, "ClusterRefresher.run ERROR " + ex.getMessage()+"\n\n"+Logger.getStackTrace(ex), -1, -1);
			Logger.error(ClusterRefresher.class, ex);
		}
	}

	/**
	 * Vykona refresh DB objektu volanim getInstance(true)
	 * @param nodeName
	 * @param className
	 */
	private void refreshObject(String nodeName, String className)
	{
		try
		{
			Logger.debug(ClusterRefresher.class, "invoking: " + className);

			long now = Tools.getNow();

			if (className.startsWith("sk.iway.iwcm.doc.DocDB-"))
			{
				//je to ciastkovy update DocDB, musime zavolat
				int docId = Tools.getIntValue(className.substring(className.indexOf('-')+1), -1);
				if (docId > 0)
				{
					DocDB.getInstance().updateInternalCaches(docId);
				}
			}
         	else if (className.startsWith("sk.iway.iwcm.system.ConfDB-"))
			{
				//je to ciastkovy update ConfDB
				String name = className.substring(className.indexOf('-')+1);
				if (Tools.isNotEmpty(name))
				{
                    ConfDB.refreshVariable(name);
				}
			}
         	else if (className.startsWith("sk.iway.iwcm.Cache-"))
			{
				//je to ciastkovy update Cache
				String name = className.substring(className.indexOf('-')+1);
				if (Tools.isNotEmpty(name))
				{
					if ("delAll".equals(name)) {
						Cache.getInstance().clearAll();
						DB.resetHtmlAllowedFields();
					}
					else Cache.getInstance().removeObject(name, false);
				}
			}
			else if (className.startsWith("sk.iway.iwcm.Cache:startsWithName-"))
			{
				//je to ciastkovy update Cache
				String name = className.substring(className.indexOf('-')+1);
				Cache.getInstance().removeObjectStartsWithName(name, false);
			}
			else if (className.startsWith("sk.iway.iwcm.tags.CombineTag-"))
			{
				//aktualizacia timestampu
				long timestamp = Tools.getLongValue(className.substring(className.indexOf('-')+1), Tools.getNow());
				//zmen version tag
				CombineTag.setVersion(timestamp);
			}
			else
			{
				String classNameFixed = className;
				String methodName = "getInstance";
				Class<?>[] parameterTypes = new Class[] {boolean.class};
				Object[] arguments = new Object[] {true};

				if (className.contains("-"))
				{
					//je to ciastkovy update objektu, na to mame speci metodu g
					classNameFixed = className.substring(0, className.indexOf('-'));
					long id = Tools.getLongValue(className.substring(className.indexOf('-')+1), 1);
					methodName = "refresh";
					parameterTypes = new Class[] {long.class};
					arguments = new Object[] {id};
				}

				Class<?> c = Class.forName(classNameFixed);
				Method m = c.getMethod(methodName, parameterTypes);

				now = Tools.getNow();
				m.invoke(c, arguments);

				Cache.getInstance().removeObjectStartsWithName(classNameFixed, false);
			}

			cleanClassStatus(nodeName, className, now);
		}
		catch (Exception ex)
		{
			Adminlog.add(Adminlog.TYPE_CRON, "Error invoking "+className+" error: " + ex.getMessage()  +"\n\n"+Logger.getStackTrace(ex), -1, -1);
			Logger.error(ClusterRefresher.class, ex);
		}
	}

	/**
	 * Po spravnom refreshe databazy vymaze zaznam z DB
	 * @param nodeName
	 * @param className
	 * @param now - datum vykonania refreshu
	 */
	private void cleanClassStatus(String nodeName, String className, long now)
	{
		new SimpleQuery().execute("DELETE FROM cluster_refresher WHERE node_name=? AND class_name=? AND refresh_time<=?",
				nodeName, className, new Timestamp(now));
	}

	/**
	 * Zmaze stare zaznamy (starsie ako 5 hodin) ktore sa kopia kvoli nebeziacim nodom (napr. pasive, alebo aktualne vypnutym) aby sa nenafukovala DB
	 */
	private void cleanOldStatusAllNodes()
	{
		Calendar cal = Calendar.getInstance();
		if ("auto".equals(Constants.getString("clusterNames")))
		{
			//v auto mode ponechavame udalosti len za poslednych 30 minut (nema zmysel viac)
			cal.add(Calendar.MINUTE, -30);
		}
		else
		{
			cal.add(Calendar.MINUTE, -5*60);
		}

		new SimpleQuery().execute("DELETE FROM cluster_refresher WHERE refresh_time<=?", new Timestamp(cal.getTimeInMillis()));

		//zmas aj stare konf. premenne statXXX
		if ("auto".equals(Constants.getString("clusterNames"))) {
			new SimpleQuery().execute("DELETE FROM "+ConfDB.CONF_TABLE_NAME+" WHERE name like ? AND date_changed<=?", "statDistinctUsers-%", new Timestamp(cal.getTimeInMillis()));
			new SimpleQuery().execute("DELETE FROM "+ConfDB.CONF_TABLE_NAME+" WHERE name like ? AND date_changed<=?", "statSessions-%", new Timestamp(cal.getTimeInMillis()));
		}
	}

	public void cancelTask()
	{
		Logger.println(ClusterRefresher.class, "destroying cluster refresher");
		if (timer != null) timer.cancel();
		this.cancel();
	}

	private static int getLastExecutedAutoId() {
		return lastExecutedAutoId;
	}

	private static void setLastExecutedAutoId(int lastExecutedAutoId) {
		ClusterRefresher.lastExecutedAutoId = lastExecutedAutoId;
	}


}