PageRenderTime 25ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 1ms

/worldguard-legacy/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/TableCache.java

https://gitlab.com/igserfurtmcschulserver/CustomWorldGuard
Java | 262 lines | 157 code | 36 blank | 69 comment | 12 complexity | 0acf11f5fabbeedfc48e43cc4e41a4a3 MD5 | raw file
  1. /*
  2. * WorldGuard, a suite of tools for Minecraft
  3. * Copyright (C) sk89q <http://www.sk89q.com>
  4. * Copyright (C) WorldGuard team and contributors
  5. *
  6. * This program is free software: you can redistribute it and/or modify it
  7. * under the terms of the GNU Lesser General Public License as published by the
  8. * Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but WITHOUT
  12. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  14. * for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.sk89q.worldguard.protection.managers.storage.sql;
  20. import com.google.common.collect.Lists;
  21. import com.sk89q.worldguard.internal.util.sql.StatementUtils;
  22. import com.sk89q.worldguard.util.io.Closer;
  23. import com.sk89q.worldguard.util.sql.DataSourceConfig;
  24. import javax.annotation.Nullable;
  25. import java.sql.Connection;
  26. import java.sql.PreparedStatement;
  27. import java.sql.ResultSet;
  28. import java.sql.SQLException;
  29. import java.sql.Statement;
  30. import java.util.ArrayList;
  31. import java.util.Collection;
  32. import java.util.HashMap;
  33. import java.util.List;
  34. import java.util.Map;
  35. import java.util.UUID;
  36. import java.util.logging.Logger;
  37. import static com.google.common.base.Preconditions.checkNotNull;
  38. /**
  39. * Stores a cache of entries from a table for fast lookup and
  40. * creates new rows whenever required.
  41. *
  42. * @param <V> the type of entry
  43. */
  44. abstract class TableCache<V> {
  45. private static final Logger log = Logger.getLogger(TableCache.class.getCanonicalName());
  46. private static final int MAX_NUMBER_PER_QUERY = 100;
  47. private static final Object LOCK = new Object();
  48. private final Map<V, Integer> cache = new HashMap<V, Integer>();
  49. private final DataSourceConfig config;
  50. private final Connection conn;
  51. private final String tableName;
  52. private final String fieldName;
  53. /**
  54. * Create a new instance.
  55. *
  56. * @param config the data source config
  57. * @param conn the connection to use
  58. * @param tableName the table name
  59. * @param fieldName the field name
  60. */
  61. protected TableCache(DataSourceConfig config, Connection conn, String tableName, String fieldName) {
  62. this.config = config;
  63. this.conn = conn;
  64. this.tableName = tableName;
  65. this.fieldName = fieldName;
  66. }
  67. /**
  68. * Convert from the type to the string representation.
  69. *
  70. * @param o the object
  71. * @return the string representation
  72. */
  73. protected abstract String fromType(V o);
  74. /**
  75. * Convert from the string representation to the type.
  76. *
  77. * @param o the string
  78. * @return the object
  79. */
  80. protected abstract V toType(String o);
  81. /**
  82. * Convert the object to the version that is stored as a key in the map.
  83. *
  84. * @param object the object
  85. * @return the key version
  86. */
  87. protected abstract V toKey(V object);
  88. @Nullable
  89. public Integer find(V object) {
  90. return cache.get(object);
  91. }
  92. /**
  93. * Fetch from the database rows that match the given entries, otherwise
  94. * create new entries and assign them an ID.
  95. *
  96. * @param entries a list of entries
  97. * @throws SQLException thrown on SQL error
  98. */
  99. public void fetch(Collection<V> entries) throws SQLException {
  100. synchronized (LOCK) { // Lock across all cache instances
  101. checkNotNull(entries);
  102. // Get a list of missing entries
  103. List<V> fetchList = new ArrayList<V>();
  104. for (V entry : entries) {
  105. if (!cache.containsKey(toKey(entry))) {
  106. fetchList.add(entry);
  107. }
  108. }
  109. if (fetchList.isEmpty()) {
  110. return; // Nothing to do
  111. }
  112. // Search for entries
  113. for (List<V> partition : Lists.partition(fetchList, MAX_NUMBER_PER_QUERY)) {
  114. Closer closer = Closer.create();
  115. try {
  116. PreparedStatement statement = closer.register(conn.prepareStatement(
  117. String.format(
  118. "SELECT id, " + fieldName + " " +
  119. "FROM `" + config.getTablePrefix() + tableName + "` " +
  120. "WHERE " + fieldName + " IN (%s)",
  121. StatementUtils.preparePlaceHolders(partition.size()))));
  122. String[] values = new String[partition.size()];
  123. int i = 0;
  124. for (V entry : partition) {
  125. values[i] = fromType(entry);
  126. i++;
  127. }
  128. StatementUtils.setValues(statement, values);
  129. ResultSet results = closer.register(statement.executeQuery());
  130. while (results.next()) {
  131. cache.put(toKey(toType(results.getString(fieldName))), results.getInt("id"));
  132. }
  133. } finally {
  134. closer.closeQuietly();
  135. }
  136. }
  137. List<V> missing = new ArrayList<V>();
  138. for (V entry : fetchList) {
  139. if (!cache.containsKey(toKey(entry))) {
  140. missing.add(entry);
  141. }
  142. }
  143. // Insert entries that are missing
  144. if (!missing.isEmpty()) {
  145. Closer closer = Closer.create();
  146. try {
  147. PreparedStatement statement = closer.register(conn.prepareStatement(
  148. "INSERT INTO `" + config.getTablePrefix() + tableName + "` (id, " + fieldName + ") VALUES (null, ?)",
  149. Statement.RETURN_GENERATED_KEYS));
  150. for (V entry : missing) {
  151. statement.setString(1, fromType(entry));
  152. statement.execute();
  153. ResultSet generatedKeys = statement.getGeneratedKeys();
  154. if (generatedKeys.next()) {
  155. cache.put(toKey(entry), generatedKeys.getInt(1));
  156. } else {
  157. log.warning("Could not get the database ID for entry " + entry);
  158. }
  159. }
  160. } finally {
  161. closer.closeQuietly();
  162. }
  163. }
  164. }
  165. }
  166. /**
  167. * An index of user rows that utilize the name field.
  168. */
  169. static class UserNameCache extends TableCache<String> {
  170. protected UserNameCache(DataSourceConfig config, Connection conn) {
  171. super(config, conn, "user", "name");
  172. }
  173. @Override
  174. protected String fromType(String o) {
  175. return o;
  176. }
  177. @Override
  178. protected String toType(String o) {
  179. return o;
  180. }
  181. @Override
  182. protected String toKey(String object) {
  183. return object.toLowerCase();
  184. }
  185. }
  186. /**
  187. * An index of user rows that utilize the UUID field.
  188. */
  189. static class UserUuidCache extends TableCache<UUID> {
  190. protected UserUuidCache(DataSourceConfig config, Connection conn) {
  191. super(config, conn, "user", "uuid");
  192. }
  193. @Override
  194. protected String fromType(UUID o) {
  195. return o.toString();
  196. }
  197. @Override
  198. protected UUID toType(String o) {
  199. return UUID.fromString(o);
  200. }
  201. @Override
  202. protected UUID toKey(UUID object) {
  203. return object;
  204. }
  205. }
  206. /**
  207. * An index of group rows.
  208. */
  209. static class GroupNameCache extends TableCache<String> {
  210. protected GroupNameCache(DataSourceConfig config, Connection conn) {
  211. super(config, conn, "group", "name");
  212. }
  213. @Override
  214. protected String fromType(String o) {
  215. return o;
  216. }
  217. @Override
  218. protected String toType(String o) {
  219. return o;
  220. }
  221. @Override
  222. protected String toKey(String object) {
  223. return object.toLowerCase();
  224. }
  225. }
  226. }