Create SQL data source utils class

- Extract logic used in SQLite and MySQL for logging and closing SQL objects
    - Decided to leave buildAuthFromResultSet methods individually as this might be more implementation-specific
- Rename DataSource#close to DataSource#closeConnection to fix conflict with static import
This commit is contained in:
ljacqu 2017-04-18 22:55:39 +02:00
parent 04ca36fe53
commit 07633a89c8
9 changed files with 226 additions and 69 deletions

View File

@ -152,7 +152,7 @@ public class CacheDataSource implements DataSource {
}
@Override
public void close() {
public void closeConnection() {
executorService.shutdown();
try {
executorService.awaitTermination(5, TimeUnit.SECONDS);
@ -160,7 +160,7 @@ public class CacheDataSource implements DataSource {
ConsoleLogger.logException("Could not close executor service:", e);
}
cachedAuths.invalidateAll();
source.close();
source.closeConnection();
}
@Override

View File

@ -130,7 +130,7 @@ public interface DataSource extends Reloadable {
/**
* Close the underlying connections to the data source.
*/
void close();
void closeConnection();
/**
* Return the data source type.

View File

@ -270,7 +270,7 @@ public class FlatFile implements DataSource {
}
@Override
public synchronized void close() {
public void closeConnection() {
}
@Override

View File

@ -29,6 +29,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static fr.xephi.authme.datasource.SqlDataSourceUtils.close;
import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException;
public class MySQL implements DataSource {
private boolean useSsl;
@ -74,7 +77,7 @@ public class MySQL implements DataSource {
try {
checkTablesAndColumns();
} catch (SQLException e) {
close();
closeConnection();
ConsoleLogger.logException("Can't initialize the MySQL database:", e);
ConsoleLogger.warning("Please check your database settings in the config.yml file!");
throw e;
@ -742,7 +745,7 @@ public class MySQL implements DataSource {
}
@Override
public void close() {
public void closeConnection() {
if (ds != null && !ds.isClosed()) {
ds.close();
}
@ -1039,29 +1042,4 @@ public class MySQL implements DataSource {
ConsoleLogger.warning("You may have entries with invalid timestamps. Please check your data "
+ "before purging. " + changedRows + " rows were migrated from seconds to milliseconds.");
}
private static void logSqlException(SQLException e) {
ConsoleLogger.logException("Error during SQL operation:", e);
}
private static void close(ResultSet rs) {
try {
if (rs != null && !rs.isClosed()) {
rs.close();
}
} catch (SQLException e) {
ConsoleLogger.logException("Could not close ResultSet", e);
}
}
private static void close(PreparedStatement pst) {
try {
if (pst != null && !pst.isClosed()) {
pst.close();
}
} catch (SQLException e) {
ConsoleLogger.logException("Could not close PreparedStatement", e);
}
}
}

View File

@ -21,6 +21,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static fr.xephi.authme.datasource.SqlDataSourceUtils.close;
import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException;
/**
*/
public class SQLite implements DataSource {
@ -60,10 +63,6 @@ public class SQLite implements DataSource {
this.con = connection;
}
private static void logSqlException(SQLException e) {
ConsoleLogger.logException("Error while executing SQL statement:", e);
}
private void connect() throws ClassNotFoundException, SQLException {
Class.forName("org.sqlite.JDBC");
ConsoleLogger.info("SQLite driver loaded");
@ -384,7 +383,7 @@ public class SQLite implements DataSource {
}
@Override
public void close() {
public void closeConnection() {
try {
if (con != null && !con.isClosed()) {
con.close();
@ -394,36 +393,6 @@ public class SQLite implements DataSource {
}
}
private static void close(Statement st) {
if (st != null) {
try {
st.close();
} catch (SQLException ex) {
logSqlException(ex);
}
}
}
private static void close(Connection con) {
if (con != null) {
try {
con.close();
} catch (SQLException ex) {
logSqlException(ex);
}
}
}
private static void close(ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException ex) {
logSqlException(ex);
}
}
}
@Override
public List<String> getAllAuthsByIp(String ip) {
PreparedStatement pst = null;

View File

@ -0,0 +1,75 @@
package fr.xephi.authme.datasource;
import fr.xephi.authme.ConsoleLogger;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* Utilities for SQL data sources.
*/
final class SqlDataSourceUtils {
private SqlDataSourceUtils() {
}
/**
* Logs a SQL exception.
*
* @param e the exception to log
*/
static void logSqlException(SQLException e) {
ConsoleLogger.logException("Error during SQL operation:", e);
}
// We use overloaded close() methods instead of one close(AutoCloseable) method in order to limit the
// checked exceptions to SQLException, which is the only checked exception these classes throw.
/**
* Closes a {@link ResultSet} safely.
*
* @param rs the result set to close
*/
static void close(ResultSet rs) {
try {
if (rs != null && !rs.isClosed()) {
rs.close();
}
} catch (SQLException e) {
ConsoleLogger.logException("Could not close ResultSet", e);
}
}
/**
* Closes a {@link Statement} safely.
*
* @param st the statement set to close
*/
static void close(Statement st) {
try {
if (st != null && !st.isClosed()) {
st.close();
}
} catch (SQLException e) {
ConsoleLogger.logException("Could not close Statement", e);
}
}
/**
* Closes a {@link Connection} safely.
*
* @param con the connection set to close
*/
static void close(Connection con) {
try {
if (con != null && !con.isClosed()) {
con.close();
}
} catch (SQLException e) {
ConsoleLogger.logException("Could not close Connection", e);
}
}
}

View File

@ -70,7 +70,7 @@ public class TaskCloser implements Runnable {
}
if (dataSource != null) {
dataSource.close();
dataSource.closeConnection();
}
}

View File

@ -0,0 +1,135 @@
package fr.xephi.authme.datasource;
import fr.xephi.authme.TestHelper;
import org.junit.Before;
import org.junit.Test;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import static org.hamcrest.Matchers.containsString;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
import static org.mockito.Mockito.verify;
/**
* Test for {@link SqlDataSourceUtils}.
*/
public class SqlDataSourceUtilsTest {
private Logger logger;
@Before
public void initLogger() {
logger = TestHelper.setupLogger();
}
@Test
public void shouldHaveHiddenConstructor() {
TestHelper.validateHasOnlyPrivateEmptyConstructor(SqlDataSourceUtils.class);
}
@Test
public void shouldLogException() {
// given
String msg = "Hocus pocus did not work";
SQLException ex = new SQLException(msg);
// when
SqlDataSourceUtils.logSqlException(ex);
// then
verify(logger).warning(argThat(containsString(msg)));
}
@Test
public void shouldCloseStatement() throws SQLException {
// given
Statement st = mock(Statement.class);
// when
SqlDataSourceUtils.close(st);
// then
verify(st).close();
}
@Test
public void shouldHandleExceptionFromStatement() throws SQLException {
// given
Statement st = mock(Statement.class);
doThrow(SQLException.class).when(st).close();
// when
SqlDataSourceUtils.close(st);
// then
verify(logger).warning(anyString());
}
@Test
public void shouldCloseResultSet() throws SQLException {
// given
ResultSet rs = mock(ResultSet.class);
// when
SqlDataSourceUtils.close(rs);
// then
verify(rs).close();
}
@Test
public void shouldHandleExceptionFromResultSet() throws SQLException {
// given
ResultSet rs = mock(ResultSet.class);
doThrow(SQLException.class).when(rs).close();
// when
SqlDataSourceUtils.close(rs);
// then
verify(logger).warning(anyString());
}
@Test
public void shouldCloseConnection() throws SQLException {
// given
Connection con = mock(Connection.class);
// when
SqlDataSourceUtils.close(con);
// then
verify(con).close();
}
@Test
public void shouldHandleExceptionFromConnection() throws SQLException {
// given
Connection con = mock(Connection.class);
doThrow(SQLException.class).when(con).close();
// when
SqlDataSourceUtils.close(con);
// then
verify(logger).warning(anyString());
}
@Test
public void shouldHandleNullArgument() {
// given / when
SqlDataSourceUtils.close((Statement) null);
SqlDataSourceUtils.close((ResultSet) null);
SqlDataSourceUtils.close((Connection) null);
// then - nothing happens
}
}

View File

@ -78,7 +78,7 @@ public class TaskCloserTest {
verify(bukkitScheduler, times(3)).isCurrentlyRunning(taskIds.capture());
assertThat(taskIds.getAllValues(), contains(ACTIVE_WORKERS_ID[0], ACTIVE_WORKERS_ID[1], ACTIVE_WORKERS_ID[1]));
verify(taskCloser, times(2)).sleep();
verify(dataSource).close();
verify(dataSource).closeConnection();
}
@Test
@ -97,7 +97,7 @@ public class TaskCloserTest {
verify(bukkitScheduler, times(3)).isQueued(anyInt());
verify(bukkitScheduler, times(61)).isCurrentlyRunning(anyInt());
verify(taskCloser, times(60)).sleep();
verify(dataSource).close();
verify(dataSource).closeConnection();
}
@Test