package com.linkedin.databus2.core.seq;
import com.linkedin.databus.core.util.ConfigBuilder;
import com.linkedin.databus.core.util.InvalidConfigException;
import com.linkedin.databus2.core.DatabusException;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.log4j.Logger;
import java.beans.PropertyVetoException;
import java.sql.*;
import java.util.concurrent.atomic.AtomicLong;
/**
*
*/
public class MysqlMaxSCNHandler implements MaxSCNReaderWriter {
private static final Logger LOG = Logger.getLogger(MysqlMaxSCNHandler.class.getName());
private final StaticConfig staticConfig;
private final AtomicLong _flushCounter;
private final AtomicLong _scn;
private final ComboPooledDataSource cpds;
public MysqlMaxSCNHandler(StaticConfig staticConfig, ComboPooledDataSource cpds){
this.staticConfig = staticConfig;
_flushCounter = new AtomicLong(0);
_scn = new AtomicLong(0);
this.cpds = cpds;
}
public static MysqlMaxSCNHandler create(StaticConfig config) throws DatabusException {
ComboPooledDataSource cpds = createConnectionPool(config);
MysqlMaxSCNHandler handler = new MysqlMaxSCNHandler(config, cpds);
handler.loadInitialValue(cpds);
return handler;
}
private void loadInitialValue(ComboPooledDataSource cpds) throws DatabusException {
Statement stmt = null;
Connection connection = null;
try {
connection = cpds.getConnection();
stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(staticConfig.getGetSCNQuery());
if(!rs.isBeforeFirst()){
LOG.info("Initial max SCN does not exist in datastore. Defaulting to initial value from configuration: "
+ staticConfig.getInitVal());
_scn.set(staticConfig.getInitVal());
writeScnToDataStore();
return;
}
while(rs.next()) {
_scn.set(rs.getLong(staticConfig.getScnColumnName()));
}
rs.close();
} catch (SQLException e) {
throw new DatabusException("unable to load initial SCN value",e);
} finally {
if(stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
}
}
}
}
private static ComboPooledDataSource createConnectionPool(StaticConfig staticConfig) throws DatabusException {
ComboPooledDataSource cpds = new ComboPooledDataSource();
try {
cpds.setDriverClass( staticConfig.getDriverClass() );
} catch (PropertyVetoException e) {
throw new DatabusException("Unable to create connection pool",e);
}
cpds.setJdbcUrl(staticConfig.getJdbcUrl());
cpds.setUser(staticConfig.getDbUser());
cpds.setPassword(staticConfig.getDbPassword());
//TODO : Default connection pool setting, make it configurable
return cpds;
}
@Override
public long getMaxScn() throws DatabusException {
return _scn.get();
}
@Override
public void saveMaxScn(long scn) throws DatabusException {
_scn.set(scn);
long ctr = _flushCounter.addAndGet(1);
if(ctr % staticConfig.getFlushItvl() == 0) {
LOG.info("Flushing counter:" + ctr);
writeScnToDataStore();
}
}
private void writeScnToDataStore() throws DatabusException {
LOG.info("save max scn to mysql");
PreparedStatement stmt = null;
Connection connection = null;
try {
connection = cpds.getConnection();
stmt = connection.prepareStatement(staticConfig.getUpsertSCNQuery());
stmt.setLong(1,_scn.get());
int i = stmt.executeUpdate();
LOG.info("scn inserted "+_scn.get() +", success : "+i);
} catch (SQLException e) {
LOG.error("Could not persist SCN value "+_scn.get(),e);
throw new DatabusException("Could not persist SCN value "+_scn.get(),e);
}finally {
if(stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
}
}
}
}
public void destroy() throws DatabusException {
LOG.info("destory() called, saving scn file before shutting down.");
writeScnToDataStore();
cpds.close();
}
public static class Config implements ConfigBuilder<StaticConfig>
{
private String jdbcUrl= "jdbc:mysql://localhost:3306/databus";
private String scnTable = "databus_scn_store";
private String driverClass = "com.mysql.jdbc.Driver";
private String dbUser = "root";
private String dbPassword = "";
private Long flushItvl = 1L;
private Long initVal = 0L;
private String upsertSCNQuery = "insert into databus_scn_store (max_scn) values (?)";
private String getSCNQuery = "select max_scn from databus_scn_store order by updated_at desc limit 1";
private String scnColumnName = "max_scn";
public String getScnColumnName() {
return scnColumnName;
}
public void setScnColumnName(String scnColumnName) {
this.scnColumnName = scnColumnName;
}
public Long getInitVal() {
return initVal;
}
public void setInitVal(Long initVal) {
this.initVal = initVal;
}
public String getUpsertSCNQuery() {
return upsertSCNQuery;
}
public void setUpsertSCNQuery(String upsertSCNQuery) {
this.upsertSCNQuery = upsertSCNQuery;
}
public String getGetSCNQuery() {
return getSCNQuery;
}
public void setGetSCNQuery(String getSCNQuery) {
this.getSCNQuery = getSCNQuery;
}
public Long getFlushItvl() {
return flushItvl;
}
public void setFlushItvl(Long flushItvl) {
this.flushItvl = flushItvl;
}
public String getDbUser() {
return dbUser;
}
public void setDbUser(String dbUser) {
this.dbUser = dbUser;
}
public String getDbPassword() {
return dbPassword;
}
public void setDbPassword(String dbPassword) {
this.dbPassword = dbPassword;
}
public String getJdbcUrl() {
return jdbcUrl;
}
public void setJdbcUrl(String jdbcUrl) {
this.jdbcUrl = jdbcUrl;
}
public String getScnTable() {
return scnTable;
}
public void setScnTable(String scnTable) {
this.scnTable = scnTable;
}
public String getDriverClass() {
return driverClass;
}
public void setDriverClass(String driverClass) {
this.driverClass = driverClass;
}
@Override
public StaticConfig build() throws InvalidConfigException {
//TODO : verify
return new StaticConfig(jdbcUrl,scnTable, driverClass, dbUser, dbPassword, flushItvl, initVal ,
upsertSCNQuery, getSCNQuery ,scnColumnName);
}
}
public static class StaticConfig{
private String driverClass = "com.mysql.jdbc.Driver";
private String jdbcUrl;
private String scnTable;
private String dbUser;
private String dbPassword;
private Long flushItvl;
private Long initVal;
private String upsertSCNQuery;
private String getSCNQuery;
private String scnColumnName;
public String getScnColumnName() {
return scnColumnName;
}
public Long getInitVal() {
return initVal;
}
public String getUpsertSCNQuery() {
return upsertSCNQuery;
}
public String getGetSCNQuery() {
return getSCNQuery;
}
public Long getFlushItvl() {
return flushItvl;
}
public String getDriverClass() {
return driverClass;
}
public String getJdbcUrl() {
return jdbcUrl;
}
public String getScnTable() {
return scnTable;
}
public String getDbUser() {
return dbUser;
}
public String getDbPassword() {
return dbPassword;
}
public StaticConfig(String host, String table, String driverClass, String dbUser, String dbPassword, long flushItvl, long initVal,
String upsertSCNQuery, String getSCNQuery, String scnColumnName){
this.jdbcUrl = host;
this.scnTable = table;
this.driverClass = driverClass;
this.dbUser = dbUser;
this.dbPassword = dbPassword;
this.flushItvl = flushItvl;
this.initVal = initVal;
this.upsertSCNQuery = upsertSCNQuery;
this.getSCNQuery = getSCNQuery;
this.scnColumnName = scnColumnName;
}
}
}