UserData is now copied to avoid null pointer exception after some data
is removed from cache while analysis is running.
Additionally added access and stopAccessing to analysis in case the copy
constructor is somehow flawed, to prevent the data from getting cleared
while analysis is being run.
Also added clear prevention to inspect cache if the data is accessed.
This commit is contained in:
Rsl1122 2017-03-09 20:52:05 +02:00
parent ef57c59d43
commit 236a2bd702
5 changed files with 132 additions and 21 deletions

View File

@ -6,9 +6,11 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
import main.java.com.djrapitops.plan.Plan;
import main.java.com.djrapitops.plan.api.Gender;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
@ -127,6 +129,43 @@ public class UserData {
playerKills = new ArrayList<>();
}
/**
* Creates a new UserData object with copied values.
* @param data UserData to copy into the new object.
*/
public UserData(UserData data) {
this.accessing = 0;
this.uuid = data.getUuid();
this.location = data.getLocation();
this.locations = new ArrayList<>();
locations.addAll(data.getLocations());
this.ips = new HashSet<>();
ips.addAll(data.getIps());
this.nicknames = new HashSet<>();
nicknames.addAll(data.getNicknames());
this.lastNick = data.getLastNick();
this.registered = data.getRegistered();
this.lastPlayed = data.getLastPlayed();
this.playTime = data.getPlayTime();
this.loginTimes = data.getLoginTimes();
this.timesKicked = data.getTimesKicked();
this.lastGmSwapTime = data.getLastGmSwapTime();
this.lastGamemode = data.getLastGamemode();
this.gmTimes = new HashMap<>();
gmTimes.putAll(data.getGmTimes());
this.isOp = data.isOp();
this.isBanned = data.isBanned();
DemographicsData dem = data.getDemData();
this.demData = new DemographicsData(dem.getAge(), dem.getGender(), dem.getGeoLocation());
this.mobKills = data.getMobKills();
this.playerKills = data.getPlayerKills();
this.deaths = data.getDeaths();
this.name = data.getName();
this.isOnline = data.isOnline();
this.sessions = new ArrayList<>();
sessions.addAll(data.getSessions());
}
/**
*
* @param ip
@ -630,4 +669,75 @@ public class UserData {
public void setLastNick(String lastNick) {
this.lastNick = lastNick;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final UserData other = (UserData) obj;
if (this.accessing != other.accessing) {
return false;
}
if (this.registered != other.registered) {
return false;
}
if (this.lastPlayed != other.lastPlayed) {
return false;
}
if (this.playTime != other.playTime) {
return false;
}
if (this.loginTimes != other.loginTimes) {
return false;
}
if (this.timesKicked != other.timesKicked) {
return false;
}
if (this.lastGmSwapTime != other.lastGmSwapTime) {
return false;
}
if (this.mobKills != other.mobKills) {
return false;
}
if (this.deaths != other.deaths) {
return false;
}
if (!Objects.equals(this.lastNick, other.lastNick)) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
if (!Objects.equals(this.uuid, other.uuid)) {
return false;
}
if (!Objects.equals(this.ips, other.ips)) {
return false;
}
if (!Objects.equals(this.nicknames, other.nicknames)) {
return false;
}
if (this.lastGamemode != other.lastGamemode) {
return false;
}
if (!Objects.equals(this.gmTimes, other.gmTimes)) {
return false;
}
if (!Objects.equals(this.playerKills, other.playerKills)) {
return false;
}
if (!Objects.equals(this.sessions, other.sessions)) {
return false;
}
return true;
}
}

View File

@ -55,7 +55,7 @@ public class InspectCacheHandler {
DBCallableProcessor cacher = new DBCallableProcessor() {
@Override
public void process(UserData data) {
cache.put(uuid, data);
cache.put(uuid, new UserData(data));
}
};
handler.getUserDataForProcessing(cacher, uuid, false);
@ -69,7 +69,9 @@ public class InspectCacheHandler {
@Override
public void run() {
if (new Date().toInstant().getEpochSecond() - clearTimes.get(uuid) < 30) {
clearFomCache(uuid);
if (!cache.get(uuid).isAccessed()) {
clearFomCache(uuid);
}
} else {
this.cancel();
}

View File

@ -6,6 +6,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import main.java.com.djrapitops.plan.Phrase;
import main.java.com.djrapitops.plan.Plan;
import main.java.com.djrapitops.plan.Settings;
@ -30,8 +31,6 @@ public class Analysis {
private final Plan plugin;
private final InspectCacheHandler inspectCache;
private final List<UserData> rawData;
private final List<UUID> added;
/**
* Class Constructor.
@ -40,9 +39,7 @@ public class Analysis {
*/
public Analysis(Plan plugin) {
this.plugin = plugin;
this.inspectCache = plugin.getInspectCache();
rawData = new ArrayList<>();
added = new ArrayList<>();
this.inspectCache = plugin.getInspectCache();
}
/**
@ -50,13 +47,13 @@ public class Analysis {
*
* First retrieves all Offlineplayers and checks those that are in the
* database. Then Runs a new Analysis Task Asyncronously. Saves AnalysisData
* to the provided Cache. Saves all UserData to InspectCache for 8 minutes.
* to the provided Cache. Saves all UserData to InspectCache for 15 minutes.
*
* @param analysisCache Cache that the data is saved to.
*/
public void analyze(AnalysisCacheHandler analysisCache) {
rawData.clear();
added.clear();
List<UserData> rawData = new ArrayList<>();
List<UUID> added = new ArrayList<>();
log(Phrase.ANALYSIS_START + "");
List<UUID> uuids = fetchPlayersInDB();
@ -68,7 +65,7 @@ public class Analysis {
BukkitTask asyncAnalysisTask = (new BukkitRunnable() {
@Override
public void run() {
uuids.stream().filter(uuid -> uuid != null).forEach((uuid) -> {
uuids.stream().forEach((uuid) -> {
inspectCache.cache(uuid, 15);
});
log(Phrase.ANALYSIS_FETCH_DATA + "");
@ -80,6 +77,7 @@ public class Analysis {
UserData userData = inspectCache.getFromCache(uuid);
if (userData != null) {
rawData.add(userData);
userData.access();
added.add(uuid);
}
}
@ -135,12 +133,12 @@ public class Analysis {
sorted.addTotalDeaths(uData.getDeaths());
sorted.getSessiondata().addAll(uData.getSessions());
sorted.getRegistered().add(uData.getRegistered());
uData.stopAccessing();
} catch (NullPointerException e) {
plugin.logError(Phrase.DATA_CORRUPTION_WARN.parse(uData.getUuid() + ""));
plugin.toLog(this.getClass().getName(), e);
}
});
// Analyze & Save RawAnalysisData to AnalysisData
createPlayerActivityGraphs(analysisData, sorted.getSessiondata(), sorted.getRegistered());
@ -166,7 +164,7 @@ public class Analysis {
analysisData.setRefreshDate(new Date().getTime());
analysisCache.cache(analysisData);
plugin.log(Phrase.ANALYSIS_COMPLETE + "");
plugin.log(Phrase.ANALYSIS_COMPLETE + "");
this.cancel();
}
@ -240,19 +238,19 @@ public class Analysis {
}
private List<UUID> fetchPlayersInDB() {
final List<UUID> uuids = new ArrayList<>();
log(Phrase.ANALYSIS_FETCH_PLAYERS + "");
try {
// final List<UUID> uuids = new ArrayList<>();
log(Phrase.ANALYSIS_FETCH_PLAYERS + "");
Set<UUID> savedUUIDs = plugin.getDB().getSavedUUIDs();
savedUUIDs.parallelStream()
List<UUID> uuids = savedUUIDs.parallelStream()
.filter(uuid -> uuid != null)
.filter((uuid) -> (getOfflinePlayer(uuid).hasPlayedBefore()))
.forEach((uuid) -> {
uuids.add(uuid);
});
.collect(Collectors.toList());
return uuids;
} catch (Exception e) {
plugin.toLog(this.getClass().getName(), e);
}
return uuids;
return new ArrayList<>();
}
private void log(String msg) {

View File

@ -75,6 +75,7 @@ public class AnalysisUtils {
int newPlayers = 0;
if (!registered.isEmpty()) {
newPlayers = registered.stream()
.filter((reg) -> (reg != null))
.filter((reg) -> (reg > now - scale))
.map((_item) -> 1).reduce(newPlayers, Integer::sum);
}

View File

@ -1,7 +1,7 @@
name: Plan
author: Rsl1122
main: main.java.com.djrapitops.plan.Plan
version: 2.8.0
version: 2.8.1
softdepend:
- OnTime