map = _codeToString.get(type);
+ String value = (map == null) ? null : map.get(code);
+ if (value == null) {
+ // our value may have been mapped on a different server, so refresh
+ // this mapping table from the database; then try again
+ try {
+ loadStringCodes(type);
+ } catch (PersistenceException pe) {
+ log.log(Level.WARNING, "Failed to reload string codes " +
+ "[type=" + type + ", code=" + code + "].", pe);
+ }
+ map = _codeToString.get(type);
+ value = (map == null) ? null : map.get(code);
+ if (value == null) {
+ log.warning("Missing reverse maping [type=" + type +
+ ", code=" + code + "].");
+ }
+ }
+ return value;
+ }
+
+ /**
+ * This is only used for testing. Do not call this method.
+ */
+ public void clearMapping (Stat.Type type, String value)
+ {
+ int ocode = _stringToCode.get(type).remove(value);
+ _codeToString.get(type).remove(ocode);
+ }
+
+ /**
+ * Invokes the supplied processor on every stat in the database of the specified type.
+ *
+ * Note: the stats database will inevitable be extremely large (one row for every
+ * paying player and one for non-payers less than six months old; millions of rows if the game
+ * is at all successful). Don't call this method willy nilly and the summarized results should
+ * be cached for at least 12 hours. (Stats don't change that frequently in the aggregate.)
+ */
+ public void processStats (final Processor processor, Stat.Type type)
+ throws PersistenceException
+ {
+ final Stat stat = type.newStat();
+ final String query = "select STATS.PLAYER_ID, ACCOUNT_NAME, HANDLE, CREATED, " +
+ "SESSION_MINUTES, STAT_DATA from STATS, PLAYERS " +
+ "where PLAYERS.PLAYER_ID = STATS.PLAYER_ID and STAT_CODE = " + type.code();
+ execute(new Operation