Guest User

Untitled

a guest
May 1st, 2016
30
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.35 KB | None | 0 0
  1. package com.rsps.core.network.mysql;
  2.  
  3. import java.sql.Connection;
  4. import java.sql.DriverManager;
  5. import java.sql.PreparedStatement;
  6. import java.sql.ResultSet;
  7. import java.sql.SQLException;
  8. import java.util.LinkedList;
  9. import java.util.Queue;
  10. import java.util.Random;
  11. import java.util.concurrent.Callable;
  12. import java.util.concurrent.ExecutorService;
  13. import java.util.concurrent.Executors;
  14. import java.util.concurrent.Future;
  15. import java.util.logging.Level;
  16. import java.util.logging.Logger;
  17.  
  18. /**
  19. * Handles a database connection.
  20. *
  21. * @author Rene Roosen
  22. */
  23. public class ExternalDatabase implements Runnable {
  24.  
  25. /**
  26. * Represents a connection in the pool.
  27. *
  28. * @author Rene Roosen
  29. */
  30. private class DatabaseConnection {
  31.  
  32. /**
  33. * The database connection.
  34. */
  35. private Connection connection;
  36.  
  37. /**
  38. * The last time the connection was pinged.
  39. */
  40. private long lastPing = System.currentTimeMillis();
  41.  
  42. /**
  43. * The time of the last query
  44. */
  45. private long lastQuery;
  46.  
  47. /**
  48. * The last reconnection, we periodically delete the connections, to
  49. * release their resources
  50. */
  51. private long lastReconnect = System.currentTimeMillis() + new Random().nextInt(120000); // Initial
  52. // Time
  53. // setting,
  54. // because otherwise
  55. // they close/reopen all
  56. // at the same time
  57.  
  58. /**
  59. * The database password.
  60. */
  61. private final String password;
  62.  
  63. /**
  64. * The sate of the connection.
  65. */
  66. private State state = State.INACTIVE;
  67.  
  68. /**
  69. * The url of the database.
  70. */
  71. private final String url;
  72.  
  73. /**
  74. * The database username.
  75. */
  76. private final String username;
  77.  
  78. /**
  79. * Initialises the MySQL driver and database settings.
  80. *
  81. * @param username
  82. * @param password
  83. * @param url
  84. */
  85. private DatabaseConnection(String username, String password, String url) {
  86. this.username = username;
  87. this.password = password;
  88. this.url = "jdbc:mysql://" + url;
  89. }
  90.  
  91. /**
  92. * Closes the datbase connection.
  93. */
  94. private void close() {
  95. if (connection != null) {
  96. try {
  97. connection.close();
  98. } catch (SQLException e) {
  99. logger.log(Level.WARNING, "Error closing database connection", e);
  100. }
  101.  
  102. setState(State.INACTIVE);
  103. }
  104. }
  105.  
  106. /**
  107. * Attempts to connect to the database.
  108. *
  109. * @return
  110. */
  111. private boolean connect() {
  112. setState(State.INACTIVE);
  113.  
  114. try {
  115. connection = DriverManager.getConnection(url, username, password);
  116. } catch (Exception e) {
  117. logger.log(Level.SEVERE, "Error connecting to MySQL database (" + url + ") !", e);
  118. return false;
  119. }
  120.  
  121. return true;
  122. }
  123.  
  124. /**
  125. * @return the connection.
  126. */
  127. public Connection getConnection() {
  128. return connection;
  129. }
  130.  
  131. /**
  132. * @return the state of the connection.
  133. */
  134. public State getState() {
  135. synchronized (this) {
  136. return state;
  137. }
  138. }
  139.  
  140. /**
  141. * @return true if the ping was successful.
  142. */
  143. private boolean ping() {
  144. lastPing = System.currentTimeMillis();
  145.  
  146. if ((connection != null) && ((System.currentTimeMillis() - lastReconnect) > 5000)) {
  147. try {
  148. connection.prepareStatement("SELECT 1").executeQuery();
  149. } catch (SQLException e) {
  150. return false;
  151. }
  152.  
  153. return true;
  154. }
  155.  
  156. return false;
  157. }
  158.  
  159. /**
  160. * Sets the state of the connection.
  161. *
  162. * @param state
  163. */
  164. public void setState(State state) {
  165. synchronized (this) {
  166. this.state = state;
  167. }
  168. }
  169.  
  170. }
  171.  
  172. /**
  173. * Represents a database query.
  174. *
  175. * @author Rene Roosen
  176. */
  177. private class InteralQuery implements Callable<ResultSet> {
  178.  
  179. /**
  180. * The query to be executed.
  181. */
  182. private final String query;
  183.  
  184. /**
  185. * Initialise the query.
  186. *
  187. * @param query
  188. */
  189. private InteralQuery(String query) {
  190. this.query = query;
  191. }
  192.  
  193. /**
  194. * Executes the query.
  195. *
  196. * @return
  197. */
  198. @Override
  199. public ResultSet call() {
  200.  
  201. PreparedStatement statement = null;
  202. boolean isUpdating = !query.toLowerCase().startsWith("select");
  203. DatabaseConnection pooledConnection = getPooledConnection();
  204.  
  205. if (pooledConnection == null) {
  206. if (isUpdating) {
  207. addFailedQuery(this);
  208. }
  209.  
  210. System.out.println("Unexpected: pooled connection returned null..");
  211.  
  212. return null;
  213. }
  214.  
  215. pooledConnection.setState(State.BUSY);
  216. pooledConnection.lastQuery = System.currentTimeMillis();
  217.  
  218. try {
  219. statement = pooledConnection.getConnection().prepareStatement(query);
  220.  
  221. if (isUpdating) {
  222. statement.executeUpdate();
  223. } else {
  224. return statement.executeQuery();
  225. }
  226. } catch (SQLException e) {
  227. if (isUpdating) {
  228. addFailedQuery(this);
  229. }
  230. e.printStackTrace();
  231. } finally {
  232. if ((statement != null) && isUpdating) {
  233. try {
  234. statement.close();
  235. } catch (SQLException ex) {
  236. logger.log(Level.WARNING, "Error closing statement", ex);
  237. }
  238. }
  239.  
  240. pooledConnection.setState(State.IDLE);
  241. }
  242.  
  243. return null;
  244. }
  245.  
  246. @Override
  247. public String toString() {
  248. return query;
  249. }
  250.  
  251. }
  252.  
  253. /**
  254. * The state of the connection.
  255. *
  256. * @author Rene Roosen
  257. */
  258. private static enum State {
  259.  
  260. BUSY,
  261. IDLE,
  262. INACTIVE
  263.  
  264. }
  265.  
  266. /**
  267. * The logger.
  268. */
  269. private static Logger logger = Logger.getLogger(ExternalDatabase.class.getName());
  270.  
  271. /**
  272. * Queries that failed execution.
  273. */
  274. private final Queue<InteralQuery> failedQueries = new LinkedList<>();
  275.  
  276. /**
  277. * Determines if the <code>DatabaseManager</code> is running.
  278. */
  279. private boolean isRunning = true;
  280.  
  281. /**
  282. * The interval at which connections are pinged.
  283. */
  284. private long pingInterval = 60000;
  285.  
  286. /**
  287. * The database connection pool.
  288. */
  289. private final DatabaseConnection[] pool;
  290.  
  291. /**
  292. * The work service.
  293. */
  294. private final ExecutorService workService;
  295.  
  296. /**
  297. * Initialise the database manager.
  298. *
  299. * @param username
  300. * @param password
  301. * @param url
  302. * @param poolSize
  303. */
  304. public ExternalDatabase(String username, String password, String url, int poolSize) {
  305. pool = new DatabaseConnection[poolSize];
  306. workService = Executors.newFixedThreadPool(pool.length);
  307.  
  308. for (int i = 0; i < pool.length; i++) {
  309. pool[i] = new DatabaseConnection(username, password, url);
  310. }
  311.  
  312. new Thread(this, "DatabaseConnection").start();
  313. }
  314.  
  315. /**
  316. * Adds a failed query to the list.
  317. *
  318. * @param query
  319. */
  320. private void addFailedQuery(InteralQuery query) {
  321. synchronized (this) {
  322. failedQueries.add(query);
  323. System.out.println(String.format("SQL query failed [failCount: %d]: %s", failedQueries.size(), query.query));
  324. }
  325. }
  326.  
  327. /**
  328. * Executes a database query.
  329. *
  330. * @param query
  331. */
  332. public ResultSet executeQuery(String query) {
  333. ResultSet result = null;
  334.  
  335. if (!query.toLowerCase().startsWith("select")) {
  336. workService.submit(new InteralQuery(query));
  337. } else {
  338. Future<?> future = workService.submit(new InteralQuery(query));
  339.  
  340. try {
  341. result = (ResultSet) future.get();
  342. } catch (Exception e) {
  343. logger.log(Level.WARNING, "Error executing query!", e);
  344. }
  345. }
  346.  
  347. return result;
  348. }
  349.  
  350. /**
  351. * Gets the first available connection from the pool.
  352. *
  353. * @return
  354. */
  355. public DatabaseConnection getPooledConnection() {
  356. for (DatabaseConnection connection : pool) {
  357. if (connection.getState().equals(State.IDLE)) {
  358. return connection;
  359. }
  360. }
  361.  
  362. return null;
  363. }
  364.  
  365. /**
  366. * Connect all the connections.
  367. */
  368. public void initialise() {
  369. int connectionCount = 0;
  370.  
  371. for (DatabaseConnection poolConnection : pool) {
  372. if (!poolConnection.connect()) {
  373. continue;
  374. }
  375.  
  376. connectionCount++;
  377. poolConnection.setState(State.IDLE);
  378. }
  379.  
  380. System.out.println("Succesfully opened " + connectionCount + " database connection.");
  381. }
  382.  
  383. @Override
  384. public void run() {
  385. while (isRunning) {
  386.  
  387. // Sleep a little..
  388. try {
  389. Thread.sleep(1000);
  390. } catch (InterruptedException e) {
  391. continue;
  392. }
  393.  
  394. // Ping the connections in the pool.
  395. for (DatabaseConnection connection : pool) {
  396. if ((System.currentTimeMillis() - connection.lastPing) <= pingInterval) {
  397. continue;
  398. }
  399.  
  400. synchronized (connection) {
  401. if (connection.getState() == State.IDLE) {
  402. if (((System.currentTimeMillis() - connection.lastReconnect) > 120000) && ((System.currentTimeMillis() - connection.lastQuery) > 1000)) {
  403. connection.close();
  404. connection.lastReconnect = System.currentTimeMillis();
  405. }
  406. }
  407. }
  408.  
  409. if (!connection.ping()) {
  410. boolean reconnected = connection.connect();
  411.  
  412. if (reconnected) {
  413. connection.setState(State.IDLE);
  414. }
  415. }
  416. }
  417.  
  418. // Execute queries that previously failed.
  419. synchronized (this) {
  420. if (!failedQueries.isEmpty()) {
  421. InteralQuery query;
  422.  
  423. while ((query = failedQueries.poll()) != null) {
  424. workService.submit(query);
  425. }
  426. }
  427. }
  428. }
  429.  
  430. }
  431.  
  432. /**
  433. * Sets the interval at which connections are pinged in minutes.
  434. *
  435. * @param pingInterval
  436. */
  437. public void setPingInterval(int pingInterval) {
  438. this.pingInterval = pingInterval * 60000;
  439. }
  440.  
  441. /**
  442. * Shuts down the database manager.
  443. */
  444. public void shutdown() {
  445. isRunning = false;
  446. failedQueries.clear();
  447.  
  448. if (workService != null) {
  449. workService.shutdown();
  450. }
  451.  
  452. for (DatabaseConnection connection : pool) {
  453. connection.close();
  454. }
  455. }
  456.  
  457. }
Add Comment
Please, Sign In to add comment