/**
*
*/
package com.linkedin.databus.bootstrap.common;
/*
*
* Copyright 2013 LinkedIn Corp. All rights reserved
*
* 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.
*
*/
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.log4j.Logger;
import com.linkedin.databus2.util.DBHelper;
/**
* This shall be where the connection pooling is implemented and all bootstrap processor
* shall just have a handle to this connection pool instead of creating instances of this class
*/
public class BootstrapConn
{
private Connection _bootstrapConn;
private String _url;
private boolean _autoCommit;
private int _isolationLevel;
private PreparedStatement _maxridStmt;
public static final String MODULE = BootstrapConn.class.getName();
public static final Logger LOG = Logger.getLogger(MODULE);
private static final String MAXRID_QUERY = "select maxrid from bootstrap_loginfo where srcid = ? and logid = ?";
public BootstrapConn()
{
_bootstrapConn = null;
}
public Connection getDBConn()
throws SQLException
{
if ( (null == _bootstrapConn) || (_bootstrapConn.isClosed()))
{
createNewBootstrapConnection();
}
return _bootstrapConn;
}
public String getSrcTableName(int srcId)
{
return "tab_" + srcId;
}
public String getLogTableNameToProduce(int srcId) throws SQLException
{
return "log_" + srcId + "_" + getProducerLogId(srcId);
}
public String getLogTableNameToApply(int srcId) throws SQLException
{
return "log_" + srcId + "_" + getApplyLogId(srcId);
}
public String getLogTableName(int logId, int srcId)
{
return "log_" + srcId + "_" + logId;
}
private int getLogId(int srcId, String tab) throws SQLException
{
int logid = -1;
Connection conn = getDBConn();
PreparedStatement stmt = null;
ResultSet rs = null;
try
{
stmt =
conn.prepareStatement("SELECT logid from " + tab + " where srcid = ?");
stmt.setInt(1, srcId);
rs = stmt.executeQuery();
while (rs.next())
{
logid = rs.getInt(1);
}
}
catch (SQLException e)
{
LOG.error("Error encountered while selecting logid from " + tab, e);
throw e;
} finally {
DBHelper.close(rs, stmt, null);
}
return logid;
}
private int getProducerLogId(int srcId) throws SQLException
{
return getLogId(srcId, "bootstrap_producer_state");
}
private int getApplyLogId(int srcId) throws SQLException
{
return getLogId(srcId, "bootstrap_applier_state");
}
public long executeQueryAndGetLong(String query, long defaultVal)
throws SQLException
{
Statement stmt = null;
Connection conn = getDBConn();
ResultSet rs = null;
long res = defaultVal;
try
{
stmt = conn.createStatement();
rs = stmt.executeQuery(query);
if ( rs.next() )
{
res = rs.getLong(1);
}
} finally {
DBHelper.close(rs,stmt,null);
}
return res;
}
/**
*
* @param query: that expects to return a single long
* @param defaultVal: value to be returned if there is an error in query or if a null result was found
* @param timeoutSeconds : query timeout
* @return single row result of type long;
* @throws SQLException
*/
public long executeQuerySafe(String query,long defaultVal,int timeoutSeconds) throws SQLException
{
Statement stmt = null;
Connection conn = getDBConn();
ResultSet rs = null;
long res = defaultVal;
try
{
stmt = conn.createStatement();
if (timeoutSeconds > 0)
{
stmt.setQueryTimeout(timeoutSeconds);
}
rs = stmt.executeQuery(query);
if (rs.next())
{
res = rs.getLong(1);
// NULL result is interpreted as 0 with getLong()
if (rs.wasNull())
{
res = defaultVal;
}
}
}
catch (SQLException e)
{
LOG.warn("Error executing query : " + query + " Exception=" + e.getMessage() );
LOG.warn("Returning default value: " + defaultVal);
}
finally
{
DBHelper.close(rs, stmt, null);
}
return res;
}
public void executeDDL(String sql)
throws SQLException
{
Statement stmt = null;
LOG.info("Executing DDL command :" + sql);
try
{
stmt = _bootstrapConn.createStatement();
int rs = stmt.executeUpdate(sql);
DBHelper.commit(_bootstrapConn);
LOG.info("Executed Commmand (" + sql + ") with result " + rs);
} catch (SQLException s) {
DBHelper.rollback(_bootstrapConn);
} finally {
DBHelper.close(stmt);
}
}
public void executeUpdate(String sql)
throws SQLException
{
Statement stmt = null;
LOG.info("Executing update command :" + sql);
try
{
stmt = _bootstrapConn.createStatement();
int rs = stmt.executeUpdate(sql);
DBHelper.commit(_bootstrapConn);
LOG.info("Executed Commmand (" + sql + ") with result " + rs);
} catch (SQLException s) {
DBHelper.rollback(_bootstrapConn);
} finally {
DBHelper.close(stmt);
}
}
public void initBootstrapConn(boolean autoCommit,
int isolationLevel,
String userName,
String password,
String hostName,
String dbName)
throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException
{
StringBuilder urlStr = new StringBuilder();
urlStr.append("jdbc:mysql://" + hostName);
if ( null != dbName )
urlStr.append("/" + dbName);
urlStr.append("?user=").append(userName).append("&password=").append(password);
_url = urlStr.toString();
_autoCommit = autoCommit;
_isolationLevel = isolationLevel;
Class.forName("com.mysql.jdbc.Driver").newInstance();
createNewBootstrapConnection();
}
private void createNewBootstrapConnection()
throws SQLException
{
_bootstrapConn = DriverManager.getConnection(_url);
_bootstrapConn.setAutoCommit(_autoCommit);
_bootstrapConn.setTransactionIsolation(_isolationLevel);
}
public void initBootstrapConn(boolean autoCommit,
String userName,
String password,
String hostName,
String dbName)
throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException
{
initBootstrapConn(autoCommit, java.sql.Connection.TRANSACTION_REPEATABLE_READ, userName, password, hostName, dbName);
}
public void recreateConnection()
throws SQLException
{
close();
createNewBootstrapConnection();
}
public void close()
{
DBHelper.close(_bootstrapConn);
_bootstrapConn = null;
}
/*
* Execute a test Query on MySQL DB
*/
public void executeDummyBootstrapDBQuery()
throws SQLException
{
ResultSet rs = null;
Statement stmt = null;
String query = "select * from bootstrap_sources";
try
{
stmt = _bootstrapConn.createStatement();
rs = stmt.executeQuery(query);
while(rs.next())
{}
} catch (SQLException ex) {
LOG.error("Got Exception when executing dummy Query :" + query, ex);
throw ex;
} finally {
DBHelper.close(rs,stmt, null);
}
}
/*
* Find the max row id of the log table.
* This is an approximate measure of the number of rows in the log table
*/
public long getMaxRowIdForLog(int logid, int srcid)
throws SQLException
{
long maxrid = 0;
if ( null == _maxridStmt)
{
_maxridStmt = getDBConn().prepareStatement(MAXRID_QUERY);
}
_maxridStmt.setInt(1, srcid);
_maxridStmt.setInt(2, logid);
ResultSet rs = null;
try
{
rs = _maxridStmt.executeQuery();
if ( rs.next() )
{
maxrid = rs.getLong(1);
}
} finally {
DBHelper.close(rs);
}
return maxrid;
}
}