com.techempower.data.jdbc.HikariCPConnectorFactory Maven / Gradle / Ivy
Show all versions of gemini-hikaricp Show documentation
/*******************************************************************************
* Copyright (c) 2018, TechEmpower, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name TechEmpower, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL TECHEMPOWER, INC. BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
package com.techempower.data.jdbc;
import static com.techempower.gemini.Configurator.*;
import static com.techempower.util.EnhancedProperties.*;
import java.io.*;
import java.sql.*;
import java.util.*;
import java.util.Map.*;
import com.techempower.*;
import com.techempower.asynchronous.*;
import com.techempower.data.*;
import com.techempower.helper.*;
import com.techempower.util.*;
import com.zaxxer.hikari.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An implementation of ConnectorFactory that is a thin wrapper around the
* HikariCP connection pool.
*
* The following attributes are read from the properties file for configuring
* this component:
*
* - [prefix]Enabled - whether this ConnectorFactory is enabled.
* - [prefix]PropertiesFile - where to find the hikari.properties file for
* configuring HikariCP.
*
*
* Note that the default prefix is 'db.HikariCP.', so unless you specify
* otherwise in your constructor call, you would identify the hikari.properties
* file location by setting the 'db.HikariCP.PropertiesFile' property in your
* Gemini .conf file.
*/
public class HikariCPConnectorFactory implements ConnectorFactory, Configurable, Asynchronous
{
//
// Constants.
//
public static final String DEFAULT_PROPERTY_PREFIX = "db.HikariCP.";
public static final String ENV_VAR_MACRO_START = MACRO_START + PROP_ENVIRONMENT_PREFIX;
//
// Member variables.
//
private final String propertyPrefix;
private final Logger log = LoggerFactory.getLogger(getClass());
private HikariConfig hikariConfig;
private HikariDataSource dataSource;
private boolean enabled = true;
private String hikariPropsFile = null;
private String identifierQuoteString = " ";
private DatabaseAffinity databaseAffinity;
//
// Member methods.
//
/**
* Constructor. If the propertyPrefix is passed as an empty String, the default
* "db.HikariCP." will be used.
*
* @param propertyPrefix
* the prefix to apply to all property names in the conf file; default
* is "db.HikariCP."
*/
public HikariCPConnectorFactory(String propertyPrefix)
{
// Use default prefix if an empty String is provided.
if (StringHelper.isNonEmpty(propertyPrefix))
{
this.propertyPrefix = propertyPrefix;
}
else
{
this.propertyPrefix = DEFAULT_PROPERTY_PREFIX;
}
}
@Deprecated(forRemoval = true)
public HikariCPConnectorFactory(TechEmpowerApplication application,
String propertyPrefix)
{
this(propertyPrefix);
}
/**
* Configure this component. For a HikariCPConnectorFactory, it is expected that
* the db.HikariCP.PropertiesFile will define the location of the
* hikari.properties file. See the HikariCP documentation for details on the
* values to put in hikari.properties.
*/
@Override
public void configure(EnhancedProperties rawProps)
{
// Read parameters.
final EnhancedProperties.Focus props = rawProps.focus(propertyPrefix);
enabled = props.getBoolean("Enabled", true);
hikariPropsFile = props.get("PropertiesFile", "src/main/webapp/WEB-INF/configuration/hikari.properties");
// Only proceed if this connector factory is enabled.
if (enabled)
{
// Close existing DataSource, if exists.
end();
// To support specifying environment variables inside the hikari.properties
// file, we need to find environment variable references in the property values,
// do the lookup, and pass along the environment variable values to HikariCP.
final File propFile = new File(hikariPropsFile);
final Properties hikariPropsReady = new Properties();
// Specify System.out as the default logWriter.
hikariPropsReady.put("dataSource.logWriter", new PrintWriter(System.out));
try (final InputStream is = propFile.isFile() ? new FileInputStream(propFile)
: this.getClass().getResourceAsStream(hikariPropsFile)) {
if (is != null) {
Properties hikariPropsRaw = new Properties();
hikariPropsRaw.load(is);
hikariPropsRaw.forEach((key, value) -> {
String envVarName = extractEnvironmentVariableName(value.toString());
if (envVarName != null) {
// Read the environment variable.
String envVarValue = System.getenv(envVarName);
if (envVarValue != null) {
// We have an environment variable, so use that to replace the relevant portion
// of the string.
hikariPropsReady.setProperty(key.toString(), StringHelper.replaceSubstrings(value.toString(),
ENV_VAR_MACRO_START + envVarName + MACRO_END, envVarValue));
} else {
// We do not have an environment variable, so use the original value.
hikariPropsReady.setProperty(key.toString(), value.toString());
}
} else {
// Pass along the property unchanged if not referencing an environment
// variable.
hikariPropsReady.setProperty(key.toString(), value.toString());
}
});
} else {
log.info("Cannot find property file: " + hikariPropsFile);
}
} catch (IOException io) {
log.info("Failed to read property file", io);
}
// Pass to HikariCP our prepared Properties object with any relevant environment
// variables in place.
hikariConfig = new HikariConfig(hikariPropsReady);
dataSource = new HikariDataSource(hikariConfig);
log.info("Connected: {}", dataSource.getJdbcUrl());
log.debug("HikariDataSource: {}", dataSource);
for (Entry