package com.vladmihalcea.flexypool.config; import com.vladmihalcea.flexypool.adaptor.PoolAdapterFactory; import com.vladmihalcea.flexypool.connection.ConnectionProxyFactory; import com.vladmihalcea.flexypool.event.EventListenerResolver; import com.vladmihalcea.flexypool.metric.MetricsFactory; import com.vladmihalcea.flexypool.strategy.ConnectionAcquiringStrategy; import com.vladmihalcea.flexypool.strategy.ConnectionAcquiringStrategyFactory; import com.vladmihalcea.flexypool.strategy.ConnectionAcquiringStrategyFactoryResolver; import com.vladmihalcea.flexypool.util.ClassLoaderUtils; import com.vladmihalcea.flexypool.util.JndiUtils; import com.vladmihalcea.flexypool.util.LazyJndiResolver; import com.vladmihalcea.flexypool.util.ReflectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.sql.DataSource; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Properties; /** * <code>PropertyLoader</code> - The Property Loader allows declarative configuration through the <code>flexy-pool.properties</code> file. * It loads the {@link Properties} configuration file and it's then used to create a {@link Configuration} object. * * @author Vlad Mihalcea * @since 1.2 */ public class PropertyLoader { private static final Logger LOGGER = LoggerFactory.getLogger(PropertyLoader.class); public static final String PROPERTIES_FILE_PATH = "flexy.pool.properties.path"; public static final String PROPERTIES_FILE_NAME = "flexy-pool.properties"; /** * Each Property has a well-defined key. */ public enum PropertyKey { DATA_SOURCE_UNIQUE_NAME("flexy.pool.data.source.unique.name"), DATA_SOURCE_JNDI_NAME("flexy.pool.data.source.jndi.name"), DATA_SOURCE_JNDI_LAZY_LOOKUP("flexy.pool.data.source.jndi.lazy.lookup"), DATA_SOURCE_CLASS_NAME("flexy.pool.data.source.class.name"), DATA_SOURCE_PROPERTY("flexy.pool.data.source.property."), POOL_ADAPTER_FACTORY("flexy.pool.adapter.factory"), POOL_METRICS_FACTORY("flexy.pool.metrics.factory"), POOL_CONNECTION_PROXY_FACTORY("flexy.pool.connection.proxy.factory"), POOL_METRICS_REPORTER_LOG_MILLIS("flexy.pool.metrics.reporter.log.millis"), POOL_METRICS_REPORTER_JMX_ENABLE("flexy.pool.metrics.reporter.jmx.enable"), POOL_METRICS_REPORTER_JMX_AUTO_START("flexy.pool.metrics.reporter.jmx.auto.start"), POOL_STRATEGIES_FACTORY_RESOLVER("flexy.pool.strategies.factory.resolver"), POOL_EVENT_LISTENER_RESOLVER("flexy.pool.event.listener.resolver"), POOL_TIME_THRESHOLD_CONNECTION_ACQUIRE("flexy.pool.time.threshold.connection.acquire"), POOL_TIME_THRESHOLD_CONNECTION_LEASE("flexy.pool.time.threshold.connection.lease"),; private final String key; PropertyKey(String key) { this.key = key; } public String getKey() { return key; } } private final Properties properties = new Properties(); public PropertyLoader() { load(); } /** * Load {@link Properties} from the resolved {@link InputStream} */ private void load() { InputStream propertiesInputStream = null; try { propertiesInputStream = propertiesInputStream(); if (propertiesInputStream == null) { throw new IllegalArgumentException("The properties file could not be loaded!"); } properties.load(propertiesInputStream); } catch (IOException e) { LOGGER.error("Can't load properties", e); } finally { try { if (propertiesInputStream != null) { propertiesInputStream.close(); } } catch (IOException e) { LOGGER.error("Can't close the properties InputStream", e); } } } /** * Get {@link Properties} file {@link InputStream} * * @return {@link Properties} file {@link InputStream} * @throws IOException the file couldn't be loaded properly */ private InputStream propertiesInputStream() throws IOException { String propertiesFilePath = System.getProperty(PROPERTIES_FILE_PATH); URL propertiesFileUrl = null; if (propertiesFilePath != null) { try { propertiesFileUrl = new URL(propertiesFilePath); } catch (MalformedURLException ignore) { propertiesFileUrl = ClassLoaderUtils.getResource(propertiesFilePath); if (propertiesFileUrl == null) { File f = new File(propertiesFilePath); if (f.exists() && f.isFile()) { try { propertiesFileUrl = f.toURI().toURL(); } catch (MalformedURLException e) { LOGGER.error("The property " + propertiesFilePath + " can't be resolved to either a URL/a Classpath resource or a File"); } } } } if (propertiesFileUrl != null) { return propertiesFileUrl.openStream(); } } return ClassLoaderUtils.getResourceAsStream(PROPERTIES_FILE_NAME); } /** * Get {@link DataSource} unique name * * @return {@link DataSource} unique name */ public String getUniqueName() { return properties.getProperty(PropertyKey.DATA_SOURCE_UNIQUE_NAME.getKey()); } /** * Get associated {@link DataSource}. The {@link DataSource} can either be looked up in JNDI or instantiated * from the configuration meta-data. * * @return {@link DataSource} */ public <T extends DataSource> T getDataSource() { T dataSource = jndiLookup(PropertyKey.DATA_SOURCE_JNDI_NAME); if (dataSource != null) { return dataSource; } dataSource = instantiateClass(PropertyKey.DATA_SOURCE_CLASS_NAME); if (dataSource == null) { throw new IllegalArgumentException("The " + PropertyKey.DATA_SOURCE_CLASS_NAME + " property is mandatory!"); } return applyDataSourceProperties(dataSource); } /** * Apply DataSource specific properties */ private <T extends DataSource> T applyDataSourceProperties(T dataSource) { for (Map.Entry<Object, Object> entry : properties.entrySet()) { String key = entry.getKey().toString(); String value = entry.getValue().toString(); String propertyKey = PropertyKey.DATA_SOURCE_PROPERTY.getKey(); if (key.startsWith(propertyKey)) { String dataSourceProperty = key.substring(propertyKey.length()); ReflectionUtils.invokeSetter(dataSource, dataSourceProperty, value); } } return dataSource; } /** * Get the {@link PoolAdapterFactory} * * @return {@link PoolAdapterFactory} */ public <T extends DataSource> PoolAdapterFactory<T> getPoolAdapterFactory() { return instantiateClass(PropertyKey.POOL_ADAPTER_FACTORY); } /** * Get the {@link MetricsFactory} * * @return {@link MetricsFactory} */ public MetricsFactory getMetricsFactory() { return instantiateClass(PropertyKey.POOL_METRICS_FACTORY); } /** * Get the {@link ConnectionProxyFactory} * * @return {@link ConnectionProxyFactory} */ public ConnectionProxyFactory getConnectionProxyFactory() { return instantiateClass(PropertyKey.POOL_CONNECTION_PROXY_FACTORY); } /** * Get log reporter millis * * @return log reporter millis */ public Integer getMetricLogReporterMillis() { return integerProperty(PropertyKey.POOL_METRICS_REPORTER_LOG_MILLIS); } /** * Is JMX Reporter enabled * * @return JMX Reporter enabled */ public Boolean isJmxEnabled() { return booleanProperty(PropertyKey.POOL_METRICS_REPORTER_JMX_ENABLE); } /** * Is JMX Reporter auto-started * * @return JMX Reporter auto-started */ public Boolean isJmxAutoStart() { return booleanProperty(PropertyKey.POOL_METRICS_REPORTER_JMX_AUTO_START); } /** * Is JNDI lazy lookup * * @return JNDI lazy lookup */ public boolean isJndiLazyLookup() { return Boolean.TRUE.equals(booleanProperty(PropertyKey.DATA_SOURCE_JNDI_LAZY_LOOKUP)); } /** * Get the array of {@link ConnectionAcquiringStrategyFactory} for this {@link com.vladmihalcea.flexypool.FlexyPoolDataSource} * * @return the array of {@link ConnectionAcquiringStrategyFactory} */ @SuppressWarnings("unchecked") public <T extends DataSource> List<ConnectionAcquiringStrategyFactory<? extends ConnectionAcquiringStrategy, T>> getConnectionAcquiringStrategyFactories() { ConnectionAcquiringStrategyFactoryResolver<T> connectionAcquiringStrategyFactoryResolver = instantiateClass(PropertyKey.POOL_STRATEGIES_FACTORY_RESOLVER); if (connectionAcquiringStrategyFactoryResolver != null) { return connectionAcquiringStrategyFactoryResolver.resolveFactories(); } return Collections.emptyList(); } /** * Get the event listener resolver * * @return event listener resolver */ public EventListenerResolver getEventListenerResolver() { return instantiateClass(PropertyKey.POOL_EVENT_LISTENER_RESOLVER); } /** * Get the connection acquire time threshold millis * * @return connection acquire time threshold millis */ public Long getConnectionAcquireTimeThresholdMillis() { return longProperty(PropertyKey.POOL_TIME_THRESHOLD_CONNECTION_ACQUIRE); } /** * Get the connection lease time threshold millis * * @return connection lease time threshold millis */ public Long getConnectionLeaseTimeThresholdMillis() { return longProperty(PropertyKey.POOL_TIME_THRESHOLD_CONNECTION_LEASE); } /** * Instantiate class associated to the given property key * * @param propertyKey property key * @param <T> class parameter type * @return class instance */ private <T> T instantiateClass(PropertyKey propertyKey) { T object = null; String property = properties.getProperty(propertyKey.getKey()); if (property != null) { try { Class<T> clazz = ClassLoaderUtils.loadClass(property); LOGGER.debug("Instantiate {}", clazz); object = clazz.newInstance(); } catch (ClassNotFoundException e) { LOGGER.error("Couldn't load the " + property + " class given by the " + propertyKey + " property", e); } catch (InstantiationException e) { LOGGER.error("Couldn't instantiate the " + property + " class given by the " + propertyKey + " property", e); } catch (IllegalAccessException e) { LOGGER.error("Couldn't access the " + property + " class given by the " + propertyKey + " property", e); } } return object; } /** * Get Integer property value * * @param propertyKey property key * @return Integer property value */ private Integer integerProperty(PropertyKey propertyKey) { Integer value = null; String property = properties.getProperty(propertyKey.getKey()); if (property != null) { value = Integer.valueOf(property); } return value; } /** * Get Long property value * * @param propertyKey property key * @return Long property value */ private Long longProperty(PropertyKey propertyKey) { Long value = null; String property = properties.getProperty(propertyKey.getKey()); if (property != null) { value = Long.valueOf(property); } return value; } /** * Get Boolean property value * * @param propertyKey property key * @return Boolean property value */ private Boolean booleanProperty(PropertyKey propertyKey) { Boolean value = null; String property = properties.getProperty(propertyKey.getKey()); if (property != null) { value = Boolean.valueOf(property); } return value; } /** * Lookup object from JNDI * * @param propertyKey property key * @param <T> JNDI object type * @return JNDI object */ @SuppressWarnings("unchecked") private <T> T jndiLookup(PropertyKey propertyKey) { String property = properties.getProperty(propertyKey.getKey()); if (property != null) { return isJndiLazyLookup() ? (T) LazyJndiResolver.newInstance(property, DataSource.class) : (T) JndiUtils.lookup(property); } return null; } }