PkeyGenerator.java

package sk.iway.iwcm;

import java.security.MessageDigest;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Random;

import sk.iway.iwcm.database.ComplexQuery;
import sk.iway.iwcm.database.Mapper;
import sk.iway.iwcm.database.SimpleQuery;

/**
 *  PkeyGenerator.java
 *
 *@Title        webjet
 *@Company      Interway s.r.o. (www.interway.sk)
 *@Copyright    Interway s.r.o. (c) 2001-2004
 *@author       $Author: jeeff $
 *@version      $Revision: 1.13 $
 *@created      Date: 28.9.2004 15:17:37
 *@modified     $Date: 2008/12/11 08:57:49 $
 */
public class PkeyGenerator
{
	private static final String CONTEXT_NAME = "sk.iway.iwcm.PkeyGenerator";
	private Map<String, PkeyBean> generators;
	private static Random random = new Random();

	/**
	 * Ziskanie instancie
	 * @return
	 */
	public static PkeyGenerator getInstance()
	{
		return(getInstance(false));
	}

	/**
	 * Ziskanie instancie
	 * @param forceRefresh - ak je true, nacitaju sa stavy z DB
	 * @return
	 */
	public static PkeyGenerator getInstance(boolean forceRefresh)
	{
		//		try to get it from server space
		if (forceRefresh == false)
		{
			if (Constants.getServletContext().getAttribute(CONTEXT_NAME) != null)
			{
				PkeyGenerator pkgen = (PkeyGenerator) Constants.getServletContext().getAttribute(CONTEXT_NAME);
				return (pkgen);
			}
		}
		return (new PkeyGenerator());
	}

	/**
	 * Konstruktor, nacita hodnoty z databazy
	 *
	 */
   private PkeyGenerator()
   {
   	generators = new Hashtable<>();
      pkeyReloadAll();
      Constants.getServletContext().setAttribute(CONTEXT_NAME, this);
   }

   /**
    * Ziska aktualnu maximalnu hodnotu v danej tabulke
    * @param table_name
    * @param key_name
    * @return
    */
   private long getMaxValue(String table_name, String key_name)
	{
		if (Tools.isEmpty(table_name) || Tools.isEmpty(key_name))
		{
			return -1;
		}

		String sql = "SELECT MAX(" + key_name + ") FROM " + table_name;
		try{
			return new ComplexQuery().setSql(sql).singleResult(new Mapper<Long>(){
				@Override
				public Long map(ResultSet rs) throws SQLException{
					return Long.valueOf(rs.getLong(1));
				}
			}).longValue();
		}
		catch (Exception e)
		{
			Logger.error(PkeyGenerator.class, "Error executing PkeyGenerator max value, sql=" +sql+" error="+e.getMessage());
			return -1;
		}
	}

   /**
    * Nastavi maximalnu hodnotu v tabulke pkey_generator, pouziva sa iba pri inicializacii
    * @param p
    * @param value
    * @return
    */
   private synchronized boolean setMaxValue(PkeyBean p, long value)
	{
   	try{
   		new SimpleQuery().execute("UPDATE pkey_generator SET value=? WHERE name=?", value, p.getName());
   		return true;
   	}catch (Exception e) {
   		sk.iway.iwcm.Logger.error(e);
   		return false;
		}
	}

   /**
    * Inicializacia klucov
    *
    */
   private void pkeyReloadAll()
	{
   	List<PkeyBean> pkeys = new ComplexQuery().setSql("SELECT * FROM pkey_generator").list(new Mapper<PkeyBean>()
		{
   		@Override
			public PkeyBean map(ResultSet rs) throws SQLException
			{
				PkeyBean p = new PkeyBean();
				p.setName(DB.getDbString(rs, "name"));
				p.setValue(rs.getLong("value"));
				p.setTableName(DB.getDbString(rs, "table_name"));
				p.setTablePkeyName(DB.getDbString(rs, "table_pkey_name"));
				//nastav maximalnu hodnotu na 0, aby sa to pri prvom pouziti fetchlo
				p.setMaxValue(0);
				return p;
			}
		});

   	for (PkeyBean p : pkeys)
		{
   		//uloz ho
   		generators.put(p.getName(), p);
   		Logger.println(this,"PkeyGenerator:" + p.toString());
		}
	}

   private PkeyBean getPkeyBean(String name)
   {
   	PkeyBean p = generators.get(name);
   	if (p == null)
   	{
   		Logger.error(this,"PkeyGenerator: is null - " + name);
   	}
   	return(p);
   }

   /**
    * Znova nacitanie kluca z DB (alokacia rozsahu)
    * @param keyName
    */
   private void allocate(PkeyBean p)
	{
   	int INCREMENT = Constants.getInt("pkeyGenIncrement");

		//este nebol inicializovany, skus ziskat max hodnotu z DB a opravit tabulku
		if (p.getMaxValue()==0)
		{
			long maxv = getMaxValue(p.getTableName(), p.getTablePkeyName());
			if (p.getValue() < maxv)
			{
				//uloz novu maximalnu hodnotu do databazy
				setMaxValue(p, maxv);
			}
		}

		try
		{
			long value = -1;
			Connection db_conn = DBPool.getConnection();
			try
			{
				PreparedStatement ps = db_conn.prepareStatement("UPDATE pkey_generator SET value=value+"+(Constants.getInt("pkeyGenBlockSize") * INCREMENT)+" WHERE name=?");
				try
				{
					ps.setString(1, p.getName());
					ps.execute();
					ps.close();

					ps = db_conn.prepareStatement("SELECT max(value) AS value FROM pkey_generator WHERE name=?");
					ps.setString(1, p.getName());
					ResultSet rs = ps.executeQuery();
					try
					{
						while (rs.next())
						{
							value = rs.getLong("value");
						}
					}
					finally { rs.close(); }
				}
				finally { ps.close(); }
			}
			finally { db_conn.close(); }

			p.setMaxValue(value);
			p.setValue(value - (Constants.getInt("pkeyGenBlockSize") * INCREMENT));

			//zaokruhli hodnotu podla incrementu a offsetu
			//int newValue = (((keyName.getValue() / INCREMENT) + 1) * INCREMENT) + INCREMENT_OFFSET;
			long newValue = ((((p.getValue()-1) / INCREMENT) ) * INCREMENT) + 1; //NOSONAR
			Logger.debug(this,"ZAOKRUHLENE: " + p.getValue() + "->" + newValue);
			p.setValue(newValue);

			Logger.debug(this,"PkeyGenerator ALLOCATE:" + p.toString());
		}
		catch (Exception ex)
		{
			sk.iway.iwcm.Logger.error(ex);
		}
	}

   /**
    * Ziskanie dalsej hodnoty kluca
    * @param keyName
    * @return
    */
	public static synchronized int getNextValue(String keyName)
	{
		return (int) getNextValueAsLong(keyName);
	}

	public static synchronized long getNextValueAsLong(String keyName)
	{
		long value = 1;
		PkeyGenerator pkGen = PkeyGenerator.getInstance();
		int INCREMENT_OFFSET = Constants.getInt("pkeyGenOffset");
		try
		{
			PkeyBean p = pkGen.getPkeyBean(keyName);
			if (p == null)
			{
				createNewRecord(keyName);
				PkeyGenerator pkg = PkeyGenerator.getInstance();
				pkg.pkeyReloadAll();
				return getNextValueAsLong(keyName);
			}
			int INCREMENT = Constants.getInt("pkeyGenIncrement");

			if ((p.getValue()+INCREMENT+INCREMENT_OFFSET) > p.getMaxValue())
			{
				//alokuj dalsi rozsah
				pkGen.allocate(p);
			}

			//ziskaj nasledujucu hodnotu - pri starte (inicializacii) sa zabezpeci
			//presne nastavenie podla INCREMENTU, takze tu staci zvysovat
			value = p.getValue();
			//jeeff: toto je zle, hodnotu mozeme navysovat len o INCREMENT a OFFSET pridavat len k navratovej hodnote!!
			//value += INCREMENT + INCREMENT_OFFSET;
			value += INCREMENT;
			p.setValue(value);

			//uloz to naspat do Hashtabulky (? je to treba ?)
			pkGen.generators.put(keyName, p);

			Logger.debug(PkeyGenerator.class,"PkeyGenerator NEXT:" + p.toString());
		}
		catch (Exception ex)
		{
			sk.iway.iwcm.Logger.error(ex);
		}
		return(value + INCREMENT_OFFSET);
	}

	private static void createNewRecord(String keyName)
	{
		Logger.debug(PkeyGenerator.class,"PkeyGenerator NEXT is NULL");

		//Este neexistuje zaznam v tabulke, vytvor a nacitaj
		Connection db_conn = null;
		PreparedStatement ps = null;
		try
		{
			int i = 1;
			db_conn = DBPool.getConnection();
			ps = db_conn.prepareStatement("INSERT INTO pkey_generator (name, value, table_name, table_pkey_name) VALUES (?, 1, ?, ?)");
			ps.setString(i++, keyName);
			ps.setString(i++, keyName);
			ps.setString(i++, keyName+"_id");
			ps.execute();
			ps.close();
			ps = null;
		}
		catch (Exception ex)
		{
			String message = ex.getMessage();
			if (message.contains("doesn't exist")) throw new RuntimeException("Table pkey_generator doesn't exist, please create it.");
			sk.iway.iwcm.Logger.error(ex);
		}
		finally
		{
			try
			{
				if (db_conn != null)
					db_conn.close();
				if (ps != null)
					ps.close();
			}
			catch (Exception ex2)
			{
				sk.iway.iwcm.Logger.error(ex2);
			}
		}
	}

	public static String getUniqueKey()
	{
		Object o = new Object();
		String key = Tools.getNow() + "1552" + o.hashCode() + (random.nextInt(Integer.MAX_VALUE));

		byte[] defaultBytes = key.getBytes();
		try
		{
			MessageDigest algorithm = MessageDigest.getInstance("MD5");
			algorithm.reset();
			algorithm.update(defaultBytes);
			byte[] messageDigest = algorithm.digest();

			StringBuilder hexString = new StringBuilder();
			for (int i=0;i<messageDigest.length;i++)
			{
				hexString.append(Integer.toHexString(0xFF & messageDigest[i])); //NOSONAR
			}

			Logger.debug(PkeyGenerator.class, "getUniqueKey: key="+key+" hex="+hexString.toString());

			key = hexString.toString();
		}
		catch(Exception e)
		{
			sk.iway.iwcm.Logger.error(e);
		}

		return key;
	}
}