package cc.blynk.server.db;
import cc.blynk.server.core.BlockingIOProcessor;
import cc.blynk.server.core.dao.UserKey;
import cc.blynk.server.core.model.auth.User;
import cc.blynk.server.core.model.enums.GraphType;
import cc.blynk.server.core.reporting.average.AggregationKey;
import cc.blynk.server.core.reporting.average.AggregationValue;
import cc.blynk.server.core.stats.model.Stat;
import cc.blynk.server.db.dao.*;
import cc.blynk.server.db.model.FlashedToken;
import cc.blynk.server.db.model.Purchase;
import cc.blynk.server.db.model.Redeem;
import cc.blynk.utils.ServerProperties;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.Closeable;
import java.sql.Connection;
import java.sql.Statement;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* The Blynk Project.
* Created by Dmitriy Dumanskiy.
* Created on 19.02.16.
*/
public class DBManager implements Closeable {
public static final String DB_PROPERTIES_FILENAME = "db.properties";
private static final Logger log = LogManager.getLogger(DBManager.class);
private final HikariDataSource ds;
private final BlockingIOProcessor blockingIOProcessor;
private final boolean cleanOldReporting;
public UserDBDao userDBDao;
protected ReportingDBDao reportingDBDao;
protected RedeemDBDao redeemDBDao;
protected PurchaseDBDao purchaseDBDao;
protected FlashedTokensDBDao flashedTokensDBDao;
public DBManager(BlockingIOProcessor blockingIOProcessor, boolean isEnabled) {
this(DB_PROPERTIES_FILENAME, blockingIOProcessor, isEnabled);
}
public DBManager(String propsFilename, BlockingIOProcessor blockingIOProcessor, boolean isEnabled) {
this.blockingIOProcessor = blockingIOProcessor;
if (!isEnabled) {
log.info("Separate DB storage disabled.");
this.ds = null;
this.cleanOldReporting = false;
return;
}
ServerProperties serverProperties;
try {
serverProperties = new ServerProperties(propsFilename);
if (serverProperties.size() == 0) {
throw new RuntimeException();
}
} catch (RuntimeException e) {
log.warn("No {} file found. Separate DB storage disabled.", propsFilename);
this.ds = null;
this.cleanOldReporting = false;
return;
}
HikariConfig config = initConfig(serverProperties);
log.info("DB url : {}", config.getJdbcUrl());
log.info("DB user : {}", config.getUsername());
log.info("Connecting to DB...");
HikariDataSource hikariDataSource;
try {
hikariDataSource = new HikariDataSource(config);
} catch (Exception e) {
log.error("Not able connect to DB. Skipping. Reason : {}", e.getMessage());
this.ds = null;
this.cleanOldReporting = false;
return;
}
this.ds = hikariDataSource;
this.reportingDBDao = new ReportingDBDao(hikariDataSource);
this.userDBDao = new UserDBDao(hikariDataSource);
this.redeemDBDao = new RedeemDBDao(hikariDataSource);
this.purchaseDBDao = new PurchaseDBDao(hikariDataSource);
this.flashedTokensDBDao = new FlashedTokensDBDao(hikariDataSource);
this.cleanOldReporting = serverProperties.getBoolProperty("clean.reporting");
checkDBVersion();
log.info("Connected to database successfully.");
}
private void checkDBVersion() {
try {
int dbVersion = userDBDao.getDBVersion();
if (dbVersion < 90500) {
log.error("Current Postgres version is lower than minimum required 9.5.0 version. PLEASE UPDATE YOUR DB.");
}
} catch (Exception e) {
log.error("Error getting DB version.", e.getMessage());
}
}
private HikariConfig initConfig(ServerProperties serverProperties) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(serverProperties.getProperty("jdbc.url"));
config.setUsername(serverProperties.getProperty("user"));
config.setPassword(serverProperties.getProperty("password"));
config.setAutoCommit(false);
config.setConnectionTimeout(serverProperties.getLongProperty("connection.timeout.millis"));
config.setMaximumPoolSize(3);
config.setMaxLifetime(0);
config.setConnectionTestQuery("SELECT 1");
return config;
}
public void deleteUser(UserKey userKey) {
if (isDBEnabled() && userKey != null) {
blockingIOProcessor.executeDB(() -> userDBDao.deleteUser(userKey));
}
}
public void saveUsers(ArrayList<User> users) {
if (isDBEnabled() && users.size() > 0) {
blockingIOProcessor.executeDB(() -> userDBDao.save(users));
}
}
public void insertStat(String region, Stat stat) {
if (isDBEnabled()) {
reportingDBDao.insertStat(region, stat);
}
}
public void insertReporting(Map<AggregationKey, AggregationValue> map, GraphType graphType) {
if (isDBEnabled() && map.size() > 0) {
blockingIOProcessor.executeDB(() -> reportingDBDao.insert(map, graphType));
}
}
public void insertReportingRaw(Map<AggregationKey, Object> rawData) {
if (isDBEnabled() && rawData.size() > 0) {
blockingIOProcessor.executeDB(() -> reportingDBDao.insertRawData(rawData));
}
}
public void cleanOldReportingRecords(Instant now) {
if (isDBEnabled() && cleanOldReporting) {
blockingIOProcessor.executeDB(() -> reportingDBDao.cleanOldReportingRecords(now));
}
}
public Redeem selectRedeemByToken(String token) throws Exception {
if (isDBEnabled()) {
return redeemDBDao.selectRedeemByToken(token);
}
return null;
}
public boolean updateRedeem(String email, String token) throws Exception {
return redeemDBDao.updateRedeem(email, token);
}
public void insertRedeems(List<Redeem> redeemList) {
if (isDBEnabled() && redeemList.size() > 0) {
redeemDBDao.insertRedeems(redeemList);
}
}
public FlashedToken selectFlashedToken(String token) {
if (isDBEnabled()) {
return flashedTokensDBDao.selectFlashedToken(token);
}
return null;
}
public boolean activateFlashedToken(String token) {
return flashedTokensDBDao.activateFlashedToken(token);
}
public boolean insertFlashedTokens(FlashedToken[] flashedTokenList) throws Exception {
if (isDBEnabled() && flashedTokenList.length > 0) {
flashedTokensDBDao.insertFlashedTokens(flashedTokenList);
return true;
}
return false;
}
public void insertPurchase(Purchase purchase) {
if (isDBEnabled()) {
purchaseDBDao.insertPurchase(purchase);
}
}
public boolean isDBEnabled() {
return ds != null;
}
public void executeSQL(String sql) throws Exception {
try (Connection connection = ds.getConnection();
Statement statement = connection.createStatement()) {
statement.execute(sql);
connection.commit();
}
}
public Connection getConnection() throws Exception {
return ds.getConnection();
}
@Override
public void close() {
if (isDBEnabled()) {
System.out.println("Closing DB...");
ds.close();
}
}
}