/*
* Copyright 2010 Proofpoint, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.airlift.dbpool;
import com.google.common.io.Resources;
import com.google.common.primitives.Ints;
import io.airlift.dbpool.H2EmbeddedDataSourceConfig.Cipher;
import org.h2.jdbcx.JdbcDataSource;
import org.h2.util.ScriptReader;
import javax.inject.Inject;
import javax.sql.PooledConnection;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.Reader;
import java.net.URL;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.SECONDS;
public class H2EmbeddedDataSource extends ManagedDataSource
{
private final JdbcDataSource dataSource;
@Inject
public H2EmbeddedDataSource(H2EmbeddedDataSourceConfig config)
throws Exception
{
super(config.getMaxConnections(), config.getMaxConnectionWait());
requireNonNull(config.getFilename());
if (config.getFilename().isEmpty()) {
throw new IllegalArgumentException("filename is empty");
}
// build jdbc url connection string
StringBuilder jdbcUrlBuilder = new StringBuilder()
.append("jdbc:h2:").append(config.getFilename())
.append(";MVCC=").append(config.isMvccEnabled());
if (config.getCipher() != Cipher.NONE) {
jdbcUrlBuilder.append(";CIPHER=").append(config.getCipher());
}
String jdbcUrl = jdbcUrlBuilder.toString();
// create dataSource
dataSource = new JdbcDataSource();
dataSource.setURL(jdbcUrl);
dataSource.setUser("sa");
if (config.getCipher() != Cipher.NONE) {
dataSource.setPassword(config.getFilePassword() + " ");
}
else {
dataSource.setPassword("");
}
dataSource.setLoginTimeout(Ints.checkedCast(config.getMaxConnectionWait().roundTo(SECONDS)));
// connect to database and initialize database
Connection connection = getConnection();
try {
setConfig(connection, "CACHE_SIZE", config.getCacheSize());
setConfig(connection, "COMPRESS_LOB", config.getCompressLob());
setConfig(connection, "MAX_MEMORY_ROWS", config.getMaxMemoryRows());
setConfig(connection, "MAX_LENGTH_INPLACE_LOB", config.getMaxLengthInplaceLob());
setConfig(connection, "DB_CLOSE_DELAY ", "-1");
// handle init script
String fileName = config.getInitScript();
if (fileName != null) {
// find init script
File file = new File(fileName);
URL url;
if (file.exists()) {
url = file.toURI().toURL();
}
else {
url = getClass().getClassLoader().getResource(fileName);
}
if (url == null) {
throw new FileNotFoundException(fileName);
}
// execute init script
try (Reader reader = Resources.asCharSource(url, UTF_8).openStream()) {
ScriptReader scriptReader = new ScriptReader(reader);
for (String statement = scriptReader.readStatement(); statement != null; statement = scriptReader.readStatement()) {
executeCommand(connection, statement);
}
}
}
// run last so script can contain literals
setConfig(connection, "ALLOW_LITERALS", config.getAllowLiterals());
}
finally {
closeQuietly(connection);
}
}
@Override
protected PooledConnection createConnectionInternal()
throws SQLException
{
return dataSource.getPooledConnection();
}
private static void setConfig(Connection connection, String name, Object value)
throws SQLException
{
Statement statement = connection.createStatement();
try {
String command = String.format("SET %s %s", name, value);
int count = statement.executeUpdate(command);
if (count != 0) {
throw new SQLException("Failed to execute command: " + command);
}
}
finally {
closeQuietly(statement);
}
}
private static void executeCommand(Connection connection, String command)
throws SQLException
{
Statement statement = connection.createStatement();
try {
statement.executeUpdate(command);
}
finally {
closeQuietly(statement);
}
}
private static void closeQuietly(Statement statement)
{
try {
statement.close();
}
catch (SQLException ignored) {
}
}
private static void closeQuietly(Connection connection)
{
try {
connection.close();
}
catch (SQLException ignored) {
}
}
}