/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.metamodel.jdbc;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.commons.pool.impl.GenericObjectPool.Config;
import org.apache.metamodel.MetaModelException;
import org.apache.metamodel.query.CompiledQuery;
import org.apache.metamodel.query.DefaultCompiledQuery;
import org.apache.metamodel.query.Query;
import org.apache.metamodel.query.SelectItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implementation of {@link CompiledQuery} for JDBC which uses a
* {@link PreparedStatement} behind the scenes.
*/
final class JdbcCompiledQuery extends DefaultCompiledQuery implements CompiledQuery {
private static final Logger logger = LoggerFactory.getLogger(JdbcCompiledQuery.class);
private final String _sql;
private final Query _query;
private final GenericObjectPool<JdbcCompiledQueryLease> _pool;
private boolean _closed;
public JdbcCompiledQuery(JdbcDataContext dc, Query query) {
super(query);
_query = query;
_sql = dc.getQueryRewriter().rewriteQuery(query);
final Config config = new Config();
config.maxActive = getSystemPropertyValue(JdbcDataContext.SYSTEM_PROPERTY_COMPILED_QUERY_POOL_MAX_SIZE, -1);
config.minEvictableIdleTimeMillis = getSystemPropertyValue(
JdbcDataContext.SYSTEM_PROPERTY_COMPILED_QUERY_POOL_MIN_EVICTABLE_IDLE_TIME_MILLIS, 500);
config.timeBetweenEvictionRunsMillis = getSystemPropertyValue(
JdbcDataContext.SYSTEM_PROPERTY_COMPILED_QUERY_POOL_TIME_BETWEEN_EVICTION_RUNS_MILLIS, 1000);
_pool = new GenericObjectPool<JdbcCompiledQueryLease>(new JdbcCompiledQueryLeaseFactory(dc, _sql), config);
_closed = false;
logger.debug("Created compiled JDBC query: {}", _sql);
}
private int getSystemPropertyValue(String property, int defaultValue) {
String str = System.getProperty(property);
if (str == null) {
return defaultValue;
}
try {
return Integer.parseInt(str);
} catch (NumberFormatException e) {
logger.debug("Failed to parse system property '{}': '{}'", property, str);
return defaultValue;
}
}
public JdbcCompiledQueryLease borrowLease() {
if (logger.isDebugEnabled()) {
logger.debug("Borrowing lease. Leases (before): Active={}, Idle={}", getActiveLeases(), getIdleLeases());
}
try {
return _pool.borrowObject();
} catch (Exception e) {
throw handleError(e, "borrow lease");
}
}
public void returnLease(JdbcCompiledQueryLease lease) {
if (logger.isDebugEnabled()) {
logger.debug("Returning lease. Leases (before): Active={}, Idle={}", getActiveLeases(), getIdleLeases());
}
try {
_pool.returnObject(lease);
} catch (Exception e) {
throw handleError(e, "return lease");
}
}
private RuntimeException handleError(Exception e, String message) {
if (logger.isWarnEnabled()) {
logger.warn("Unexpected error occurred in compiled JDBC query: " + message, e);
}
if (e instanceof SQLException) {
return JdbcUtils.wrapException((SQLException) e, message);
} else if (e instanceof RuntimeException) {
return (RuntimeException) e;
} else {
return new MetaModelException(message, e);
}
}
protected int getActiveLeases() {
return _pool.getNumActive();
}
protected int getIdleLeases() {
return _pool.getNumIdle();
}
protected Query getQuery() {
return _query;
}
@Override
public String toSql() {
return _sql;
}
@Override
public void close() {
logger.debug("Closing compiled JDBC query: {}", _sql);
try {
_pool.close();
} catch (Exception e) {
throw handleError(e, "close pool of leases");
} finally {
_closed = true;
}
}
protected List<SelectItem> getSelectItems() {
return _query.getSelectClause().getItems();
}
@Override
protected void finalize() throws Throwable {
super.finalize();
if (!_closed) {
logger.warn("finalize() invoked, but DataSet is not closed. Invoking close() on {}", this);
close();
}
}
}