L2DatabaseFactory.java 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /*
  2. * This program is free software: you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation, either version 3 of the License, or (at your option) any later
  5. * version.
  6. *
  7. * This program is distributed in the hope that it will be useful, but WITHOUT
  8. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  9. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  10. * details.
  11. *
  12. * You should have received a copy of the GNU General Public License along with
  13. * this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. package com.l2jserver;
  16. import java.sql.Connection;
  17. import java.sql.SQLException;
  18. import java.util.concurrent.Executors;
  19. import java.util.concurrent.ScheduledExecutorService;
  20. import java.util.concurrent.TimeUnit;
  21. import java.util.logging.Level;
  22. import java.util.logging.Logger;
  23. import com.jolbox.bonecp.BoneCPDataSource;
  24. import com.l2jserver.gameserver.ThreadPoolManager;
  25. public class L2DatabaseFactory
  26. {
  27. static Logger _log = Logger.getLogger(L2DatabaseFactory.class.getName());
  28. public static enum ProviderType
  29. {
  30. MySql, MsSql
  31. }
  32. // =========================================================
  33. // Data Field
  34. private static ScheduledExecutorService _executor;
  35. private ProviderType _providerType;
  36. private BoneCPDataSource _source;
  37. // =========================================================
  38. // Constructor
  39. public L2DatabaseFactory()
  40. {
  41. _log.info("Initializing BoneCP [ version: databaseDriver -> " + Config.DATABASE_DRIVER + ", jdbcUrl -> " + Config.DATABASE_URL + ", maxConnectionsPerPartition -> " + Config.DATABASE_MAX_CONNECTIONS + ", username -> " + Config.DATABASE_LOGIN + ", password -> " + Config.DATABASE_PASSWORD + " ]");
  42. try
  43. {
  44. _source = new BoneCPDataSource();
  45. _source.getConfig().setDefaultAutoCommit(true);
  46. _source.getConfig().setPoolAvailabilityThreshold(10);
  47. _source.getConfig().setMinConnectionsPerPartition(10);
  48. _source.getConfig().setMaxConnectionsPerPartition(Config.DATABASE_MAX_CONNECTIONS);
  49. _source.setPartitionCount(3);
  50. _source.setAcquireRetryAttempts(0); // try to obtain connections indefinitely (0 = never quit)
  51. _source.setAcquireRetryDelayInMs(500); // 500 miliseconds wait before try to acquire connection again
  52. // if pool is exhausted
  53. _source.setAcquireIncrement(5); // if pool is exhausted, get 5 more connections at a time
  54. // cause there is a "long" delay on acquire connection
  55. // so taking more than one connection at once will make connection pooling
  56. // more effective.
  57. _source.setConnectionTimeoutInMs(0);
  58. // testing OnCheckin used with IdleConnectionTestPeriod is faster than testing on checkout
  59. _source.setIdleConnectionTestPeriodInMinutes(1); // test idle connection every 60 sec
  60. _source.setIdleMaxAgeInSeconds(1800);
  61. _source.setTransactionRecoveryEnabled(true);
  62. _source.setDriverClass(Config.DATABASE_DRIVER);
  63. _source.setJdbcUrl(Config.DATABASE_URL);
  64. _source.setUsername(Config.DATABASE_LOGIN);
  65. _source.setPassword(Config.DATABASE_PASSWORD);
  66. /* Test the connection */
  67. _source.getConnection().close();
  68. if (Config.DEBUG)
  69. _log.fine("Database Connection Working");
  70. if (Config.DATABASE_DRIVER.toLowerCase().contains("microsoft"))
  71. _providerType = ProviderType.MsSql;
  72. else
  73. _providerType = ProviderType.MySql;
  74. }
  75. catch (Exception e)
  76. {
  77. if (Config.DEBUG)
  78. _log.fine("Database Connection FAILED");
  79. throw new Error("L2DatabaseFactory: Failed to init database connections: " + e.getMessage(), e);
  80. }
  81. }
  82. // =========================================================
  83. // Method - Public
  84. public final String prepQuerySelect(String[] fields, String tableName, String whereClause, boolean returnOnlyTopRecord)
  85. {
  86. String msSqlTop1 = "";
  87. String mySqlTop1 = "";
  88. if (returnOnlyTopRecord)
  89. {
  90. if (getProviderType() == ProviderType.MsSql)
  91. msSqlTop1 = " Top 1 ";
  92. if (getProviderType() == ProviderType.MySql)
  93. mySqlTop1 = " Limit 1 ";
  94. }
  95. String query = "SELECT " + msSqlTop1 + safetyString(fields) + " FROM " + tableName + " WHERE " + whereClause + mySqlTop1;
  96. return query;
  97. }
  98. public void shutdown()
  99. {
  100. try
  101. {
  102. _source.close();
  103. _source = null;
  104. }
  105. catch (Exception e)
  106. {
  107. _log.log(Level.WARNING, e.getMessage(), e);
  108. }
  109. }
  110. public final String safetyString(String... whatToCheck)
  111. {
  112. // NOTE: Use brace as a safty precaution just incase name is a reserved word
  113. final char braceLeft;
  114. final char braceRight;
  115. if (getProviderType() == ProviderType.MsSql)
  116. {
  117. braceLeft = '[';
  118. braceRight = ']';
  119. }
  120. else
  121. {
  122. braceLeft = '`';
  123. braceRight = '`';
  124. }
  125. int length = 0;
  126. for (String word : whatToCheck)
  127. {
  128. length += word.length() + 4;
  129. }
  130. final StringBuilder sbResult = new StringBuilder(length);
  131. for (String word : whatToCheck)
  132. {
  133. if (sbResult.length() > 0)
  134. {
  135. sbResult.append(", ");
  136. }
  137. sbResult.append(braceLeft);
  138. sbResult.append(word);
  139. sbResult.append(braceRight);
  140. }
  141. return sbResult.toString();
  142. }
  143. public Connection getConnection()
  144. {
  145. Connection con = null;
  146. while (con == null)
  147. {
  148. try
  149. {
  150. con = _source.getConnection();
  151. if (Server.serverMode == Server.MODE_GAMESERVER)
  152. ThreadPoolManager.getInstance().scheduleGeneral(new ConnectionCloser(con, new RuntimeException()), 60000);
  153. else
  154. getExecutor().schedule(new ConnectionCloser(con, new RuntimeException()), 60, TimeUnit.SECONDS);
  155. }
  156. catch (SQLException e)
  157. {
  158. _log.log(Level.WARNING, "L2DatabaseFactory: getConnection() failed, trying again " + e.getMessage(), e);
  159. }
  160. }
  161. return con;
  162. }
  163. private static class ConnectionCloser implements Runnable
  164. {
  165. private Connection c;
  166. private RuntimeException exp;
  167. public ConnectionCloser(Connection con, RuntimeException e)
  168. {
  169. c = con;
  170. exp = e;
  171. }
  172. /* (non-Javadoc)
  173. * @see java.lang.Runnable#run()
  174. */
  175. @Override
  176. public void run()
  177. {
  178. try
  179. {
  180. if (!c.isClosed())
  181. {
  182. _log.log(Level.WARNING, "Unclosed connection! Trace: " + exp.getStackTrace()[1], exp);
  183. }
  184. }
  185. catch (SQLException e)
  186. {
  187. _log.log(Level.WARNING, "", e);
  188. }
  189. }
  190. }
  191. public static void close(Connection con)
  192. {
  193. if (con == null)
  194. return;
  195. try
  196. {
  197. con.close();
  198. }
  199. catch (SQLException e)
  200. {
  201. _log.log(Level.WARNING, "Failed to close database connection!", e);
  202. }
  203. }
  204. private static ScheduledExecutorService getExecutor()
  205. {
  206. if (_executor == null)
  207. {
  208. synchronized (L2DatabaseFactory.class)
  209. {
  210. if (_executor == null)
  211. _executor = Executors.newSingleThreadScheduledExecutor();
  212. }
  213. }
  214. return _executor;
  215. }
  216. public int getBusyConnectionCount()
  217. {
  218. return _source.getTotalLeased();
  219. }
  220. public final ProviderType getProviderType()
  221. {
  222. return _providerType;
  223. }
  224. private static final class SingletonHolder
  225. {
  226. private static final L2DatabaseFactory INSTANCE = new L2DatabaseFactory();
  227. }
  228. public static L2DatabaseFactory getInstance()
  229. {
  230. return SingletonHolder.INSTANCE;
  231. }
  232. }