package com.fitbank.common.conectivity; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.math.BigDecimal; import java.net.URL; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.Date; import java.sql.SQLException; import java.sql.Timestamp; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.SimpleDateFormat; import java.util.*; import org.apache.commons.collections.IteratorUtils; import org.apache.commons.io.IOUtils; import org.apache.log4j.Logger; import org.hibernate.MappingException; import org.hibernate.Session; import org.hibernate.SessionBuilder; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import org.hibernate.dialect.Dialect; import org.hibernate.internal.SessionFactoryImpl; import org.hibernate.internal.SessionImpl; import org.hibernate.mapping.Column; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; import org.hibernate.mapping.SimpleValue; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; import org.hibernate.stat.Statistics; import org.hibernate.type.Type; import com.fitbank.common.Helper; import com.fitbank.common.exception.FitbankException; import com.fitbank.common.helper.FormatDates; import com.fitbank.common.logger.CallerFinder; import com.fitbank.common.logger.FitbankLogger; import com.fitbank.common.properties.PropertiesHandler; import com.fitbank.util.IterableEnumeration; import com.fitbank.util.classpath.ClassPathUtils; public final class HbSession { /** * Key para genrar un map de los beans mapeados en el archivo de * hbconfiguration. */ public static final String NAME = "NAMES"; /** * Key que alamacena el numero de sessiones abiertas en el sessionfactory. */ public static final String OPEN = "OPEN"; /** * Key que alamacena el numero de sessiones cerradas en el sessionfactory. */ public static final String CLOSED = "CLOSED"; /** * Key que almacena la hora en la que se genero el sessionfactory . */ public static final String START_TIME = "START TIME"; /** * Contiene el valor del modulo que sera usado para la informacion de la sesion */ private static final String MODULE = "Fitbank-V2.0"; private static final Logger LOGGER = FitbankLogger.getLogger(); private static final String HBM_XML = ".hbm.xml"; private static final String JPA_TXT = ".jpa.txt"; private static final org.apache.commons.configuration.Configuration CONFIG = PropertiesHandler.getConfig("persistence"); private static HbSession instance = null; private SessionFactory sessionFactory; private ServiceRegistry serviceRegistry; private Configuration cfg; private final boolean traceStatistics = CONFIG.getBoolean(HbSession.class.getName() + ".TRACE_STATISTICS", false); /** * Permite establecer los datos que se asignaran a la sesion */ private static boolean developerMode = CONFIG.getBoolean("com.fitbank.common.DEVELOPER_MODE", false); /** * Listado de Persistencias que no deben ser cargados al sistema */ private static List NO_LOAD_PERSISTENCE = Arrays.asList(CONFIG.getString(HbSession.class.getName() + ".NO_LOAD_PERSISTENCE", "").split(",")); private final Map SESSION_FACTORY_STATS = new HashMap(); /** * Obtiene una instancia de HbSession. * * @return HbSession actualmente armado. */ public static HbSession getInstance() { return build(null, (String[]) null); } /** * Obtiene una instancia de HbSession en base a una lista de entidades. * * @param classNames Lista de entidades * @return HbSession actualmente armado. */ public static HbSession getInstance(String... classNames) { return build(null, classNames); } /** * Construye un HbSession dados los parametros indicados. * * @param classPath Classpath donde se buscará los beanes. * @param classNames Nombres de los beanes que se quieren cargar * @return Un HbSession */ public static synchronized HbSession build(String classPath, String... classNames) { boolean classNamesEmpty = classNames == null || classNames.length == 0; if ((classPath != null || !classNamesEmpty) && instance != null) { instance.closeSession(); instance = null; } else if (instance != null) { return instance; } // Determinar classpath usando el parámetro, el path de persistencia // o el classpath del sistema, en ese orden if (classPath == null) { classPath = PropertiesHandler.getSystemPath("persistence"); if (classPath != null) { File dir = new File(classPath); if (dir.isDirectory()) { for (File file : dir.listFiles()) { if (file.isFile() && file.getName().endsWith(".jar")) { ClassPathUtils.addToClassLoader(file); } } } else { classPath = null; } } } if (classPath == null) { classPath = System.getProperty("java.class.path"); } // Leer del archivo META-INF/services/persistence-classes if (classNamesEmpty && !CONFIG.getBoolean(HbSession.class.getName() + ".SEARCH_CLASS_PATH")) { Set classNamesSet = new HashSet(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); try { String archivo = "META-INF/services/persistence-classes"; Enumeration resources = cl.getResources(archivo); for (URL url : IterableEnumeration.get(resources)) { InputStream stream = url.openStream(); String classes = IOUtils.toString(stream, "UTF-8"); classNamesSet.addAll(Arrays.asList(classes.split("\n"))); } } catch (IOException ex) { LOGGER.warn("No se pudo leer el archivo persistence-classes", ex); } classNames = classNamesSet.toArray(new String[0]); } instance = new HbSession(classPath, classNames); return instance; } private HbSession(String classPath, String... classNames) { this.buildSessionFactory(classPath, classNames); } /** * Obtiene la configuracion usada para el SessionFactory * * @return Configuration usado para armar el SessionFactory. */ public Configuration getCfg() { return this.cfg; } /** * Obtiene el sessionFactory armado. * * @return SessionFactory armado. */ public SessionFactory getSessionFactory() { return this.sessionFactory; } /** * Abre una nueva sesion para el thread de ejecucion actual. * Si ya existe una sesion ya abierta para esta sesion actual, reusa dicha * sesion. * * @return Session nueva o reusada. */ public Session openSession() { LOGGER.debug("Abriendo nueva sesión desde " + CallerFinder.getCallerName()); //Verificar si ya existe una sesion para el thread actual y reusarla boolean openSession; try { openSession = !Helper.getSession().isOpen(); } catch (Exception e) { //NO ASUSTARSE!! Invocando una nueva sesion openSession = true; } Session session = openSession ? this.sessionFactory.openSession() : Helper.getSession(); if (openSession) { this.updateStatsValue(OPEN); this.traceStatistics("Nueva sesión abierta"); if (developerMode) { this.setSessionInfo(session); } else { Helper.setSessionInfo(); } } else { this.traceStatistics("Reusando sesión abierta"); } return session; } /** * Abre y obtiene una sesion hibernate mediante un connection jdbc. * * @param connection Connection JDBC. * @return Session */ public Session openSession(Connection connection) { LOGGER.debug("Abriendo nueva sesión mediante una conexión desde " + CallerFinder.getCallerName()); SessionBuilder sb = this.sessionFactory.withOptions(); Session session = sb.connection(connection).openSession(); this.updateStatsValue(OPEN); this.traceStatistics("Nueva sesión abierta mediante una conexión"); if (developerMode) { this.setSessionInfo(session); } return session; } /** * Obtiene y abre una sesion de hibernate sin reusar sesiones previamente abiertas * para el thread actual. * * @return Session abierta independiente del hilo actual. */ public Session forceOpenSession() { LOGGER.debug("Abriendo nueva sesión desde " + CallerFinder.getCallerName()); Session session = this.sessionFactory.openSession(); this.updateStatsValue(OPEN); this.traceStatistics("Nueva sesión abierta"); if (developerMode) { this.setSessionInfo(session); } return session; } /** * Obtiene la sesion actual bajo el mismo SessionFactory. * * @return Session actual del SessionFactory */ public Session getCurrentSession() { return this.sessionFactory.getCurrentSession(); } /** * Obtiene el dialecto con el que se armo el SessionFactory. * * @return Dialect del sessionFactory. */ public Dialect getDialect() { SessionFactoryImpl sfi = (SessionFactoryImpl) this.sessionFactory; return sfi.getDialect(); } /** * Cierra la sesion del SessionFactory. */ public void closeSession() { this.sessionFactory.close(); this.traceStatistics("SesionFactory cerrada"); } private void buildStats() { this.SESSION_FACTORY_STATS.put(OPEN, 0L); this.SESSION_FACTORY_STATS.put(CLOSED, 0L); } /** * Permite imprimir (consola) las estadisticas del uso actual de sesiones * en el SessionFactory actual. * * @param message Mensaje extra a imprimir en la consola. */ public void traceStatistics(String message) { this.traceStatistics(message, false); } private void traceStatistics(String message, boolean firstTime) { LOGGER.info(message + ". Thread [" + Thread.currentThread().getName() + "]"); if (this.traceStatistics) { LOGGER.info("########ESTADISTICAS DE LA SESION########"); if (firstTime) { Map stats = this.getSessionFactoryStatus(); Integer beanCount = ((String[]) stats.get(NAME)).length; Date upCount = (Date) stats.get(START_TIME); SimpleDateFormat sdf = new SimpleDateFormat("HH':'mm':'ss"); LOGGER.info("Beanes cargados: " + beanCount); LOGGER.info("Hora de inicio: " + sdf.format(upCount)); } else { Long openCount = this.SESSION_FACTORY_STATS.get(OPEN); Long closeCount = this.SESSION_FACTORY_STATS.get(CLOSED); LOGGER.info("Sesiones abiertas: " + openCount); LOGGER.info("Sesiones cerradas: " + closeCount); } LOGGER.info("#########################################"); } } private void setSessionInfo(Session session) { SessionFactoryImpl imp = (SessionFactoryImpl)session.getSessionFactory(); Dialect dialect = imp.getDialect(); if(!dialect.getClass().getSimpleName().startsWith("Oracle")) { LOGGER.warn("No se pueden establecer parametros de sesion a una sesion diferente de oracle"); } else { try { StackTraceElement el = (new Exception()).getStackTrace()[2]; String clientInfo = el.getClassName(); String action = el.getMethodName() + ":" + el.getLineNumber(); Connection e = ((SessionImpl)session).connection(); CallableStatement cs = e.prepareCall("{ call dbms_session.set_identifier(?) } "); cs.setString(1, Thread.currentThread().getName()); cs.execute(); cs.close(); cs = e.prepareCall("{ call dbms_application_info.set_client_info(?) } "); cs.setString(1, clientInfo); cs.execute(); cs.close(); cs = e.prepareCall("{ call sys.dbms_application_info.set_module(?, ?) }"); cs.setString(1, HbSession.MODULE); cs.setString(2, action); cs.execute(); cs.close(); } catch (SQLException var8) { LOGGER.error("\n\n\nProblemas al agregar informacion de la sesion", var8); } } } /** * Actualiza los valores de estadisticas (locales) para el sessionFactory * actual. * * @param key Valor de estadistica a cambiar. */ public synchronized void updateStatsValue(String key) { Long openSessions = this.SESSION_FACTORY_STATS.get(OPEN); Long closedSessions = this.SESSION_FACTORY_STATS.get(CLOSED); if (OPEN.equals(key)) { openSessions++; } else if (CLOSED.equals(key)) { closedSessions++; } this.SESSION_FACTORY_STATS.put(OPEN, openSessions); this.SESSION_FACTORY_STATS.put(CLOSED, closedSessions); } /** * Devuelve el estado del SessionFactory como un mapa que contiene numero * de sesiones abiertas, entities creados, numero de sessiones cerradas y * tiempo de creación del SessionFactory. * * @return Mapa con la información */ public Map getSessionFactoryStatus() { Statistics stat = this.sessionFactory.getStatistics(); Map m = new HashMap(); m.put(NAME, stat.getEntityNames()); m.put(OPEN, String.valueOf(stat.getSessionOpenCount())); m.put(CLOSED, String.valueOf(stat.getSessionCloseCount())); m.put(START_TIME, new Date(stat.getStartTime())); return m; } /** * Prepara el SessionFactory. * * @param classPath Class path donde se buscarán los beanes * @param classNames Nombres de las clases de las entidades a cargar, si es * que están disponibles, si no se especifica se cargarán todas las clases * que se encuentren en el classpath. */ private void buildSessionFactory(String classPath, String... classNames) { this.buildStats(); Set resources = new HashSet(); if (classNames == null || classNames.length == 0) { classNames = new String[0]; resources.addAll(ClassPathUtils.find(HBM_XML, classPath)); resources.addAll(ClassPathUtils.find(JPA_TXT, classPath)); } // Crear configuración LOGGER.info("Iniciando configuracion de hibernate... "); this.cfg = new Configuration(); this.cfg.configure("/HbConfiguration.xml"); for (String name : classNames) { try { LOGGER.info("Cargando: " + name); Class c = Class.forName(name); try { this.cfg.addClass(c); } catch (MappingException e) { this.cfg.addAnnotatedClass(c); } } catch (ClassNotFoundException e) { LOGGER.error(e); } catch (MappingException e) { LOGGER.error(e); } } for (String name : resources) { try { if (!NO_LOAD_PERSISTENCE.contains(name)) { LOGGER.info("Cargando: " + name); if (name.endsWith(HbSession.HBM_XML)) { this.cfg.addResource(name); } else if (name.endsWith(JPA_TXT)) { String className = name.replace(JPA_TXT, "").replace(File.separatorChar, '.'); this.cfg.addAnnotatedClass(Class.forName(className)); } } } catch (ClassNotFoundException e) { LOGGER.error(e); } catch (MappingException e) { LOGGER.error(e); } } // Crear SessionFactory LOGGER.info("Configuración lista, construyendo SessionFactory..."); this.serviceRegistry = new ServiceRegistryBuilder().applySettings(this.cfg.getProperties()).buildServiceRegistry(); this.sessionFactory = this.cfg.buildSessionFactory(this.serviceRegistry); this.traceStatistics("SessionFactory construida", true); // Arreglar carga de clobs y blobs con hibernate con oracle if (getDialect().getClass().getSimpleName().startsWith("Oracle")) { try { Class OracleDatabaseMetaData = Class.forName( "oracle.jdbc.driver.OracleDatabaseMetaData"); Method setGetLobPrecision = OracleDatabaseMetaData.getMethod( "setGetLobPrecision", boolean.class); setGetLobPrecision.invoke(null, false); } catch (ClassNotFoundException ex) { // Ignorar, no se cargó el driver de oracle } catch (NoSuchMethodException ex) { // Ignorar, es un driver nuevo } catch (Throwable ex) { LOGGER.warn("No se pudo parchear el driver de Oracle, ignorando", ex); } } } // /** * Transforma el valor al tipo de dato del tabla.campo. * * @param pBean Nombre del bean que maneja l apersistencia de una tabla. * @param pCampo Nombre de un campo que pertenece a una tabla manejada por * el bean. * @param pValue Valor del campo, puede estar en un tipo de dato distinto al * de la tabla. * @return newvalue Valor con el tipo de dato del campo en la tabla. * @throws Exception */ public Object convertValueType(String pBean, String pCampo, Object pValue) throws Exception { Object newvalue = null; PersistentClass c = this.cfg.getClassMapping(pBean); Iterator it = c.getTable().getColumnIterator(); Column column = null; boolean find = false; while (it.hasNext()) { column = (Column) it.next(); if (column.getName().equalsIgnoreCase(pCampo)) { find = true; break; } } if (!find) { throw new FitbankException("HB007", "CAMPO {0} NO DEFINIDO EN LA TABLA {1}", pCampo, c.getTable().getName()); } SimpleValue simplevalue = (SimpleValue) column.getValue(); String type = simplevalue.getTypeName(); LOGGER.debug(c.getTable().getName() + "." + column.getName() + ":" + type); if (pValue.getClass().getName().compareTo(type) == 0) { newvalue = pValue; } else { if (type.compareTo("java.lang.String") == 0) { newvalue = pValue; } if (type.compareTo("java.lang.Integer") == 0) { newvalue = Integer.valueOf(pValue.toString()); } if (type.compareTo("java.lang.Long") == 0) { newvalue = Long.valueOf(pValue.toString()); } if (type.compareTo("java.math.BigDecimal") == 0) { DecimalFormatSymbols dfs = new DecimalFormatSymbols(); dfs.setDecimalSeparator('.'); dfs.setGroupingSeparator(','); DecimalFormat df = new DecimalFormat("##############.#######", dfs); newvalue = BigDecimal.valueOf(df.parse(pValue.toString()). doubleValue()); } if (type.compareTo("java.sql.Date") == 0) { SimpleDateFormat sdf = FormatDates.getInstance(). getTransportDateFormat(); newvalue = new Date(sdf.parse(pValue.toString()).getTime()); } if (type.compareTo("java.sql.Timestamp") == 0) { try { return Timestamp.valueOf(pValue.toString()); } catch (Exception e) { } SimpleDateFormat sdf = FormatDates.getInstance(). getTransportDatetimeFormat(); try { String valor = pValue.toString(); if (valor.length() == 10) { valor += " 00:00:00.0"; } newvalue = new Timestamp(sdf.parse(valor).getTime()); } catch (Exception ex) { LOGGER.error(ex); } } } return newvalue; } /** * Enterga el bean que maneja una tabla. * * @param pTablename Nombre de la tabla. * @return Nombre de la clase que implementa el Bean */ @SuppressWarnings("unchecked") public String getBeanname(String pTablename) { Iterator itr = this.cfg.getClassMappings(); while (itr.hasNext()) { PersistentClass pc = itr.next(); if (pc.getTable().getName().equalsIgnoreCase(pTablename)) { return pc.getClassName(); } } throw new FitbankException("GEN016", "BEAN NO DEFINIDO PARA LA TABLA {0}", pTablename); } /** * Entrega el nombre del campo de una tabla. Si la tabla tiene un unico * campo que forma el primary key de la misma. * * @param pBean Nombre del bean que maneja la persistencia de una tabla. * @return Nombre del pk */ @SuppressWarnings("unchecked") public String getFieldnameByPkSingleField(Class pBean) { List columns = getKeyColumns(pBean); if (columns.size() == 1) { return columns.get(0).getName().toUpperCase(); } else if (columns.size() > 1) { throw new FitbankException("HB008", "EL BEAN {0} TIENE UN PRIMARY KEY COMPUESTO ", pBean.getName()); } else { return null; } } public List getFields(Class pBean) { List cols = new LinkedList(); cols.addAll(this.getFieldsPrimaryKey(pBean)); for (Column column : getRootColumns(pBean)) { cols.add(column.getName().toUpperCase()); } return cols; } /** * Entrega un lista de campos que forman el primary key de una tabla. * * @param pBean Nombre del bean que maneja la persistencia de una tabla. * @return lista de nombres campos del pk */ public List getFieldsPrimaryKey(Class pBean) { List lFieldspk = new LinkedList(); for (Column column : getKeyColumns(pBean)) { lFieldspk.add(column.getName().toUpperCase()); } return lFieldspk; } /** * Enterga el campo que maneja el versionamiento de un bean. * * @param pBean Clase de tipo Bean * @return Nombre del campo de versionamiento */ public String getFieldVersion(Class pBean) { PersistentClass c = getPersistentClass(pBean); if (c.getVersion() != null) { return c.getVersion().getName(); } return null; } /** * Entrega el nombre de la propiedad del Bean que hace referencia al Campo * Dado * * @param pBean Clase de tipo Bean * @param pField Campo de la Tabla * @return Nombre de la propiedad del Bean */ public String getJavaProperty(Class pBean, String pField) { // Buscar propiedades en el PK List columns = getKeyColumns(pBean); for (Column column : columns) { if (column.getName().equalsIgnoreCase(pField)) { if (columns.size() == 1) { return "pk"; } return "pk." + pField.toLowerCase(); } } // Buscar propiedades directamente for (Property property : getProperties(pBean)) { if (property.getName().equalsIgnoreCase(pField)) { return pField.toLowerCase(); } } throw new FitbankException("HB007", "EL CAMPO {0} NO EXISTE EN EL BEAN {1}", pField, pBean.getName()); } /** * Obtiene el tipo de la propiedad de un campo de un bean * * @param pBean * @param pField * @return tipo de la propiedad */ public Type getJavaPropertyType(Class pBean, String pField) { for (Property property : getProperties(pBean)) { if (property.getName().equalsIgnoreCase(pField)) { return property.getType(); } } throw new FitbankException("HB007", "EL CAMPO {0} NO EXISTE EN EL BEAN {1}", pField, pBean.getName()); } /** * Enterga el nombre de la tabla de un bean. * * @param pBean Clase de tipo Bean * @return String con el nombre de la tabla */ public String getTableName(Class pBean) { PersistentClass c = getPersistentClass(pBean); return c.getTable().getName().toUpperCase(); } /** * Verifica si un campo es un atributo del bean. * * @param pBean Nombre del bean que maneja la persistencia de una tabla * @param pField Campo de la Tabla * @return true si el campo es un atributo del bean */ public boolean isJavaProperty(Class pBean, String pField) { for (Column column : getColumns(pBean)) { if (column.getName().equalsIgnoreCase(pField)) { return true; } } return false; } /** * Indica si el campo es opcional o no. * * @param pBean * @param pField * @return true si el campo es requerido */ public boolean isNotNull(Class pBean, String pField) { for (Property property : getProperties(pBean)) { if (property.getName().equalsIgnoreCase(pField)) { return !property.isOptional(); } } throw new FitbankException("HB007", "EL CAMPO {0} NO EXISTE EN EL BEAN {1}", pField, pBean.getName()); } private PersistentClass getPersistentClass(Class clase) throws FitbankException { PersistentClass c = this.cfg.getClassMapping(clase.getName()); if (c == null) { throw new FitbankException("HB008", "LA CLASE {0} NO ES UN BEAN", clase.getName()); } return c; } @SuppressWarnings("unchecked") private List getProperties(Class clase) { PersistentClass c = getPersistentClass(clase); return IteratorUtils.toList(c.getPropertyIterator()); } @SuppressWarnings("unchecked") private List getRootColumns(Class clase) { PersistentClass c = getPersistentClass(clase); return IteratorUtils.toList(c.getRootTable().getColumnIterator()); } @SuppressWarnings("unchecked") private List getColumns(Class clase) { PersistentClass c = getPersistentClass(clase); return IteratorUtils.toList(c.getTable().getColumnIterator()); } @SuppressWarnings("unchecked") private List getKeyColumns(Class clase) { PersistentClass c = getPersistentClass(clase); return IteratorUtils.toList(c.getKey().getColumnIterator()); } // // /** * @deprecated Usar build(classPath) */ @Deprecated public static HbSession getInstance(boolean _, String classPath) { return build(classPath); } /** * @deprecated Usar openSession */ @Deprecated public Session getSession() { return this.openSession(); } /** * @deprecated Usar openSession */ @Deprecated public Session getSession(Connection connection) { return this.openSession(connection); } /** * @deprecated Usar closeSession */ @Deprecated public void close() { this.closeSession(); } // }