Subversion Repositories SmartDukaan

Rev

Rev 34690 | Blame | Compare with Previous | Last modification | View Log | RSS feed

package com.smartdukaan.cron.config;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.spice.profitmandi.dao.convertor.LocalDateJsonConverter;
import com.spice.profitmandi.dao.convertor.LocalDateTimeJsonConverter;
import com.spice.profitmandi.dao.repository.dtr.Mongo;

import com.mysql.cj.jdbc.AbandonedConnectionCleanupThread;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBuilder;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.annotation.PreDestroy;
import javax.sql.DataSource;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.Enumeration;
import java.util.Properties;

@Configuration
@EnableTransactionManagement
@ComponentScan("com.spice.profitmandi.*")
@PropertySource(value = {"classpath:shared-dev.properties", "classpath:META-INF/env.properties"}, ignoreResourceNotFound = true)
public class DBConfig {

        private static final Logger LOGGER = LogManager.getLogger(DBConfig.class);

        // Hibernate Property Keys
        private static final String HIBERNATE_DIALECT = "hibernate.dialect";
        private static final String HIBERNATE_SHOW_SQL = "hibernate.show_sql";
        private static final String HIBERNATE_FORMAT_SQL = "hibernate.format_sql";
        private static final String HIBERNATE_JDBC_BATCH_SIZE = "hibernate.jdbc.batch_size";

                // HikariCP Properties
        private static final String HIKARI_MAX_POOL_SIZE = "hikari.maximumPoolSize";
        private static final String HIKARI_MIN_IDLE = "hikari.minimumIdle";
        private static final String HIKARI_IDLE_TIMEOUT = "hikari.idleTimeout";
        private static final String HIKARI_MAX_LIFETIME = "hikari.maxLifetime";
        private static final String HIKARI_CONNECTION_TIMEOUT = "hikari.connectionTimeout";


        // Injected DB Properties
        @Value("${hibernate.driver.class}")
        private String hibernateDriverClass;

        @Value("${hibernate.url}")
        private String hibernateUrl;

        @Value("${hibernate.user.name}")
        private String hibernateUserName;

        @Value("${hibernate.password}")
        private String hibernatePassword;

        @Value("${hibernate.dialect}")
        private String hibernateDialect;

        @Value("${hibernate.show_sql}")
        private String hibernateShowSql;

        @Value("${hibernate.format_sql}")
        private String hibernateFormatSql;

        @Value("${hibernate.jdbc.batch_size}")
        private String hibernateBatchSize;

        // Mongo Config
        @Value("${mongo.host}")
        private String mongoHost;

        @Value("${content.mongo.host}")
        private String contentMongoHost;

        @Autowired
        private Environment env;

        /**
         * Primary DataSource using DriverManager (can be replaced with HikariDataSource)
         */
        @Primary
        @Bean(name = "dataSource", destroyMethod = "close")
        public HikariDataSource dataSource() {
                HikariConfig config = new HikariConfig();
                config.setDriverClassName(hibernateDriverClass);
                config.setJdbcUrl(hibernateUrl);
                config.setUsername(hibernateUserName);
                config.setPassword(hibernatePassword);

                config.setMaximumPoolSize(Integer.parseInt(env.getProperty(HIKARI_MAX_POOL_SIZE, "20")));
                config.setMinimumIdle(Integer.parseInt(env.getProperty(HIKARI_MIN_IDLE, "2")));
                config.setIdleTimeout(Long.parseLong(env.getProperty(HIKARI_IDLE_TIMEOUT, "30000")));
                config.setMaxLifetime(Long.parseLong(env.getProperty(HIKARI_MAX_LIFETIME, "1800000")));
                config.setConnectionTimeout(Long.parseLong(env.getProperty(HIKARI_CONNECTION_TIMEOUT, "30000")));

                config.setPoolName("HikariPool");
                config.setAutoCommit(true);


                HikariDataSource dataSource = new HikariDataSource(config);
                LOGGER.info("HikariDataSource initialized with JDBC URL {}", hibernateUrl);
                return dataSource;
        }

        /**
         * Configure Hibernate properties
         */
        @Bean
        public Properties getHibernateProperties() {
                Properties dbProperties = new Properties();
                dbProperties.put(HIBERNATE_DIALECT, hibernateDialect);
                dbProperties.put(HIBERNATE_SHOW_SQL, hibernateShowSql);
                dbProperties.put(HIBERNATE_FORMAT_SQL, hibernateFormatSql);
                dbProperties.put(HIBERNATE_JDBC_BATCH_SIZE, hibernateBatchSize);
                // HikariCP properties (Optional — Spring Boot auto-configures most)
                //dbProperties.put("hibernate.connection.provider_class", "org.hibernate.hikaricp.internal.HikariCPConnectionProvider");
                return dbProperties;
        }

        /**
         * Configure Hibernate SessionFactory
         */
        @Primary
        @Bean(name = "sessionFactory")
        @Autowired
        public SessionFactory getSessionFactory(DataSource dataSource) {
                LocalSessionFactoryBuilder sessionBuilder = new LocalSessionFactoryBuilder(dataSource);
                sessionBuilder.addProperties(getHibernateProperties());
                sessionBuilder.scanPackages("com.spice.profitmandi.dao.*");
                LOGGER.info("SessionFactory created");
                return sessionBuilder.buildSessionFactory();
        }

        /**
         * Transaction Manager
         */
        @Primary
        @Bean(name = "transactionManager")
        @Autowired
        public HibernateTransactionManager getTransactionManager(SessionFactory sessionFactory) {
                HibernateTransactionManager transactionManager = new HibernateTransactionManager(sessionFactory);
                LOGGER.info("TransactionManager created");
                return transactionManager;
        }

        /**
         * ObjectMapper Bean with Java 8 Time module config
         */
        @Bean
        public ObjectMapper objectMapper() {
                DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder()
                                .parseCaseInsensitive()
                                .append(DateTimeFormatter.ISO_LOCAL_DATE)
                                .optionalStart().appendLiteral('T').optionalEnd()
                                .appendLiteral(' ')
                                .append(DateTimeFormatter.ISO_LOCAL_TIME)
                                .toFormatter();

                DateTimeFormatter standardFormatter = new DateTimeFormatterBuilder()
                                .parseCaseInsensitive()
                                .append(DateTimeFormatter.ISO_LOCAL_DATE)
                                .appendLiteral('T')
                                .append(DateTimeFormatter.ISO_LOCAL_TIME)
                                .toFormatter();

                JavaTimeModule javaTimeModule = new JavaTimeModule();
                javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(standardFormatter));
                javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE));
                javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));
                javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE));

                ObjectMapper mapper = new ObjectMapper()
                                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                                .registerModule(new ParameterNamesModule())
                                .registerModule(new Jdk8Module())
                                .registerModule(javaTimeModule);

                LOGGER.info("ObjectMapper configured");
                return mapper;
        }

        /**
         * Mongo Client Bean
         */
        @Bean(destroyMethod = "close")
        public Mongo mongoClient(SessionFactory sessionFactory) {
                return new Mongo(mongoHost, contentMongoHost);
        }

        /**
         * Gson Bean for LocalDate and LocalDateTime
         */
        @Bean(name = "gson")
        public Gson gson() {
                Gson gson = new GsonBuilder()
                                .serializeNulls()
                                .registerTypeAdapter(LocalDate.class, new LocalDateJsonConverter())
                                .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeJsonConverter())
                                .create();
                LOGGER.info("Gson configured");
                return gson;
        }

        /**
         * Cleanup method to prevent memory leaks on shutdown.
         * Deregisters JDBC drivers and stops MySQL cleanup thread.
         */
        @PreDestroy
        public void cleanup() {
                LOGGER.info("DBConfig cleanup started...");

                // Stop MySQL AbandonedConnectionCleanupThread
                try {
                        AbandonedConnectionCleanupThread.checkedShutdown();
                        LOGGER.info("MySQL AbandonedConnectionCleanupThread stopped");
                } catch (Exception e) {
                        LOGGER.error("Error stopping MySQL cleanup thread", e);
                }

                // Deregister JDBC drivers loaded by this webapp's classloader
                ClassLoader cl = Thread.currentThread().getContextClassLoader();
                Enumeration<Driver> drivers = DriverManager.getDrivers();
                while (drivers.hasMoreElements()) {
                        Driver driver = drivers.nextElement();
                        if (driver.getClass().getClassLoader() == cl) {
                                try {
                                        DriverManager.deregisterDriver(driver);
                                        LOGGER.info("Deregistered JDBC driver: {}", driver);
                                } catch (SQLException e) {
                                        LOGGER.error("Error deregistering JDBC driver {}", driver, e);
                                }
                        }
                }

                LOGGER.info("DBConfig cleanup completed");
        }
}