diff --git a/src/main/java/com/alibaba/druid/filter/url/ConnectionConnectFilterChainImpl.java b/src/main/java/com/alibaba/druid/filter/url/ConnectionConnectFilterChainImpl.java index 9892dadc97..f296a29ac5 100644 --- a/src/main/java/com/alibaba/druid/filter/url/ConnectionConnectFilterChainImpl.java +++ b/src/main/java/com/alibaba/druid/filter/url/ConnectionConnectFilterChainImpl.java @@ -13,6 +13,11 @@ import java.sql.Driver; import java.sql.SQLException; import java.util.Properties; +/** + * FilterChain to create new connection with placeholder in URL + * + * @author DigitalSonic + */ public class ConnectionConnectFilterChainImpl extends FilterChainImpl { private final static Log LOG = LogFactory.getLog(ConnectionConnectFilterChainImpl.class); diff --git a/src/main/java/com/alibaba/druid/filter/url/HostAndPortHolder.java b/src/main/java/com/alibaba/druid/filter/url/HostAndPortHolder.java index 5ac334075d..a76662f811 100644 --- a/src/main/java/com/alibaba/druid/filter/url/HostAndPortHolder.java +++ b/src/main/java/com/alibaba/druid/filter/url/HostAndPortHolder.java @@ -14,6 +14,11 @@ import java.util.Map; import java.util.Properties; import java.util.Random; +/** + * A singleton object to hold Host & Port information. + * + * @author DigitalSonic + */ public class HostAndPortHolder { private static final Log LOG = LogFactory.getLog(ConnectionConnectFilterChainImpl.class); @@ -30,18 +35,22 @@ public class HostAndPortHolder { return instance; } - public static void loadFromProperties(String file) { + public static void loadProperties(String file) { Properties properties = new Properties(); if (file == null) { return; } - InputStream is; + InputStream is = null; try { LOG.debug("Trying to load " + file + " from FileSystem."); is = new FileInputStream(file); } catch(FileNotFoundException e) { LOG.debug("Trying to load " + file + " from Classpath."); - is = HostAndPortHolder.class.getResourceAsStream(file); + try { + is = HostAndPortHolder.class.getResourceAsStream(file); + } catch (Exception ex) { + LOG.warn("Can not load resource " + file, ex); + } } if (is != null) { try { diff --git a/src/main/java/com/alibaba/druid/filter/url/HostAndPortValidatorThread.java b/src/main/java/com/alibaba/druid/filter/url/HostAndPortValidatorThread.java index 05f426b685..bc460d6315 100644 --- a/src/main/java/com/alibaba/druid/filter/url/HostAndPortValidatorThread.java +++ b/src/main/java/com/alibaba/druid/filter/url/HostAndPortValidatorThread.java @@ -10,6 +10,12 @@ import java.sql.SQLException; import java.util.List; import java.util.Properties; +/** + * A thread to run the validation on each host:port. + * If some host:port is failed in the validation, we can set test flags in DruidDataSource. + * + * @author DigitalSonic + */ public class HostAndPortValidatorThread implements Runnable { private final static Log LOG = LogFactory.getLog(HostAndPortValidatorThread.class); private DruidDataSource dataSource; @@ -38,8 +44,6 @@ public class HostAndPortValidatorThread implements Runnable { } protected void check() { - Driver driver = dataSource.getRawDriver(); - Properties info = dataSource.getConnectProperties(); String url = dataSource.getRawJdbcUrl(); if (url == null || (url != null && (url.indexOf("${") == -1 || url.indexOf("}") == -1))) { @@ -49,31 +53,40 @@ public class HostAndPortValidatorThread implements Runnable { String placeHolder = url.substring(url.indexOf("${") + 2, url.indexOf("}")); List values = HostAndPortHolder.getInstance().getAll(placeHolder); for (String v : values) { - Connection conn = null; - try { - String realUrl = url.replace("${" + placeHolder + "}", v); - conn = driver.connect(realUrl, info); - dataSource.validateConnection(conn); - HostAndPortHolder.getInstance().removeBlacklist(v); - } catch (SQLException e) { - LOG.warn("Validation FAILED for " + v, e); - HostAndPortHolder.getInstance().addBlacklist(v); - modifyFlagsIfNeeded(); - } finally { - if (conn != null) { - try { - conn.close(); - } catch (SQLException e) { - LOG.error("Can not close connection for HostAndPort Validation.", e); - } - } - } + validateConnection(dataSource, placeHolder, v); } if (HostAndPortHolder.getInstance().getBlacklistSize() == 0) { resetFlagsIfNeeded(); } } + private void validateConnection(DruidDataSource dataSource, String placeHolder, String target) { + Driver driver = dataSource.getRawDriver(); + Properties info = dataSource.getConnectProperties(); + String url = dataSource.getRawJdbcUrl(); + Connection conn = null; + + try { + LOG.debug("Validating " + target + " every " + sleepSeconds + " seconds."); + String realUrl = url.replace("${" + placeHolder + "}", target); + conn = driver.connect(realUrl, info); + dataSource.validateConnection(conn); + HostAndPortHolder.getInstance().removeBlacklist(target); + } catch (SQLException e) { + LOG.warn("Validation FAILED for " + target, e); + HostAndPortHolder.getInstance().addBlacklist(target); + modifyFlagsIfNeeded(); + } finally { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + LOG.error("Can not close connection for HostAndPort Validation.", e); + } + } + } + } + private void resetFlagsIfNeeded() { if (modifyTestFlags) { dataSource.setTestOnBorrow(oldTestOnBorrow); @@ -85,7 +98,7 @@ public class HostAndPortValidatorThread implements Runnable { if (!modifyTestFlags) { return; } - if (oldTestOnReturn || oldTestOnBorrow) { + if (!oldTestOnReturn || !oldTestOnBorrow) { dataSource.setTestOnBorrow(true); dataSource.setTestOnReturn(true); } diff --git a/src/main/java/com/alibaba/druid/filter/url/JdbcUrlHostAndPortReplaceFilter.java b/src/main/java/com/alibaba/druid/filter/url/JdbcUrlHostAndPortReplaceFilter.java index b51d22e088..583105146d 100644 --- a/src/main/java/com/alibaba/druid/filter/url/JdbcUrlHostAndPortReplaceFilter.java +++ b/src/main/java/com/alibaba/druid/filter/url/JdbcUrlHostAndPortReplaceFilter.java @@ -11,22 +11,35 @@ import com.alibaba.druid.support.logging.LogFactory; import java.sql.SQLException; import java.util.Properties; +/** + * The filter to replace the placeholder in JDBC URL. + * + * @author DigitalSonic + */ public class JdbcUrlHostAndPortReplaceFilter extends FilterAdapter { private final static Log LOG = LogFactory.getLog(HostAndPortValidatorThread.class); - public static final String DEFAULT_FILE = "/druid-datasource.properties"; + public static final String DEFAULT_FILE = "datasource.properties"; private String file = DEFAULT_FILE; private int healthCheckIntervalSeconds = 0; private HostAndPortValidatorThread validatorThread; + private Thread runningValidatorThread; private boolean isValidatorThreadStarted = false; + private boolean modifyTestFlags = false; public void init() { if (file == null || file.trim().length() == 0) { file = DEFAULT_FILE; } - HostAndPortHolder.loadFromProperties(file); + HostAndPortHolder.loadProperties(file); + } + + public void close() { + if (runningValidatorThread != null) { + runningValidatorThread.interrupt(); + } } @Override @@ -44,9 +57,11 @@ public class JdbcUrlHostAndPortReplaceFilter extends FilterAdapter { if (validatorThread == null) { validatorThread = new HostAndPortValidatorThread(); validatorThread.setSleepSeconds(healthCheckIntervalSeconds); + validatorThread.setModifyTestFlags(modifyTestFlags); } validatorThread.setDataSource((DruidDataSource) dataSource); - new Thread(validatorThread).start(); + runningValidatorThread = new Thread(validatorThread); + runningValidatorThread.start(); isValidatorThreadStarted = true; } else { LOG.warn("The DataSource is not a DruidDataSource, ignore HostAndPortValidator."); @@ -64,4 +79,8 @@ public class JdbcUrlHostAndPortReplaceFilter extends FilterAdapter { public void setValidatorThread(HostAndPortValidatorThread validatorThread) { this.validatorThread = validatorThread; } + + public void setModifyTestFlags(boolean modifyTestFlags) { + this.modifyTestFlags = modifyTestFlags; + } } diff --git a/src/test/java/com/alibaba/druid/filter/url/HostAndPortHolderTest.java b/src/test/java/com/alibaba/druid/filter/url/HostAndPortHolderTest.java index e177c9a581..b4e2b2f256 100644 --- a/src/test/java/com/alibaba/druid/filter/url/HostAndPortHolderTest.java +++ b/src/test/java/com/alibaba/druid/filter/url/HostAndPortHolderTest.java @@ -7,12 +7,20 @@ import org.junit.Test; import java.util.List; import java.util.Map; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; +/** + * Test class for HostAndPortHolder + * + * @author DigitalSonic + */ public class HostAndPortHolderTest { @Before public void setUp() { - HostAndPortHolder.loadFromProperties("/com/alibaba/druid/filter/url/datasource.properties"); + HostAndPortHolder.loadProperties("/com/alibaba/druid/filter/url/datasource.properties"); } @After @@ -20,6 +28,13 @@ public class HostAndPortHolderTest { HostAndPortHolder.getInstance().clearBlacklist(); } + @Test + public void testLoadFromFileNotExisted() { + int size = HostAndPortHolder.getInstance().getHolder().size(); + HostAndPortHolder.loadProperties("FILE_NOT_FOUND"); + assertEquals(size, HostAndPortHolder.getInstance().getHolder().size()); + } + @Test public void testLoadFromProperties() { Map> holder = HostAndPortHolder.getInstance().getHolder(); diff --git a/src/test/java/com/alibaba/druid/filter/url/HostAndPortValidatorThreadTest.java b/src/test/java/com/alibaba/druid/filter/url/HostAndPortValidatorThreadTest.java new file mode 100644 index 0000000000..02b0c4c440 --- /dev/null +++ b/src/test/java/com/alibaba/druid/filter/url/HostAndPortValidatorThreadTest.java @@ -0,0 +1,40 @@ +package com.alibaba.druid.filter.url; + +import com.alibaba.druid.pool.DruidDataSource; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Test class for HostAndPortValidatorThread + * + * @author DigitalSonic + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:com/alibaba/druid/filter/url/dataSource.xml") +public class HostAndPortValidatorThreadTest { + @Autowired + private DruidDataSource dataSource; + + @Test + public void testValidateFail() throws Exception { + assertEquals(0, HostAndPortHolder.getInstance().getBlacklistSize()); + dataSource.setValidationQuery("select xxx from foo"); + Thread.sleep(20 * 1000); + assertTrue(HostAndPortHolder.getInstance().getBlacklistSize() > 0); + assertTrue(dataSource.isTestOnBorrow()); + assertTrue(dataSource.isTestOnReturn()); + + dataSource.setValidationQuery(null); + Thread.sleep(20 * 1000); + assertEquals(0, HostAndPortHolder.getInstance().getBlacklistSize()); + assertFalse(dataSource.isTestOnBorrow()); + assertFalse(dataSource.isTestOnReturn()); + } +} \ No newline at end of file diff --git a/src/test/java/com/alibaba/druid/filter/url/JdbcUrlHostAndPortReplaceFilterTest.java b/src/test/java/com/alibaba/druid/filter/url/JdbcUrlHostAndPortReplaceFilterTest.java index 6e1d4fd409..f62444b1f4 100644 --- a/src/test/java/com/alibaba/druid/filter/url/JdbcUrlHostAndPortReplaceFilterTest.java +++ b/src/test/java/com/alibaba/druid/filter/url/JdbcUrlHostAndPortReplaceFilterTest.java @@ -14,8 +14,14 @@ import java.sql.Connection; import java.util.ArrayList; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +/** + * Test class for JdbcUrlHostAndPortReplaceFilter + * + * @author DigitalSonic + */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:com/alibaba/druid/filter/url/dataSource.xml") public class JdbcUrlHostAndPortReplaceFilterTest { @@ -46,5 +52,4 @@ public class JdbcUrlHostAndPortReplaceFilterTest { c.close(); } } - } \ No newline at end of file diff --git a/src/test/resources/com/alibaba/druid/filter/url/dataSource.xml b/src/test/resources/com/alibaba/druid/filter/url/dataSource.xml index 076ab68e7d..ac2c72adfb 100644 --- a/src/test/resources/com/alibaba/druid/filter/url/dataSource.xml +++ b/src/test/resources/com/alibaba/druid/filter/url/dataSource.xml @@ -9,8 +9,11 @@ default-lazy-init="false"> - + + +