Archiviert
13
0

Update asynchronous manager and handle static senders in reports.

Dieser Commit ist enthalten in:
Kristian S. Stangeland 2013-07-06 07:51:02 +02:00
Ursprung 6847283fb3
Commit a4f81e5e9f
6 geänderte Dateien mit 623 neuen und 582 gelöschten Zeilen

Datei anzeigen

@ -217,7 +217,7 @@ public class ProtocolLibrary extends JavaPlugin {
@Override
protected Report filterReport(Object sender, Report report, boolean detailed) {
String canonicalName = ReportType.getReportName(sender.getClass(), report.getType());
String canonicalName = ReportType.getReportName(sender, report.getType());
String reportName = Iterables.getLast(Splitter.on("#").split(canonicalName)).toUpperCase();
if (config != null && config.getModificationCount() != lastModCount) {

Datei anzeigen

@ -1,80 +1,80 @@
package com.comphenix.protocol.error;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.error.Report.ReportBuilder;
/**
* Construct an error reporter that delegates to another error reporter.
* @author Kristian
*/
public class DelegatedErrorReporter implements ErrorReporter {
private final ErrorReporter delegated;
/**
* Construct a new error reporter that forwards all reports to a given reporter.
* @param delegated - the delegated reporter.
*/
public DelegatedErrorReporter(ErrorReporter delegated) {
this.delegated = delegated;
}
/**
* Retrieve the underlying error reporter.
* @return Underlying error reporter.
*/
public ErrorReporter getDelegated() {
return delegated;
}
@Override
public void reportMinimal(Plugin sender, String methodName, Throwable error) {
delegated.reportMinimal(sender, methodName, error);
}
@Override
public void reportMinimal(Plugin sender, String methodName, Throwable error, Object... parameters) {
delegated.reportMinimal(sender, methodName, error, parameters);
}
@Override
public void reportWarning(Object sender, Report report) {
Report transformed = filterReport(sender, report, false);
if (transformed != null) {
delegated.reportWarning(sender, transformed);
}
}
@Override
public void reportDetailed(Object sender, Report report) {
Report transformed = filterReport(sender, report, true);
if (transformed != null) {
delegated.reportDetailed(sender, transformed);
}
}
/**
* Invoked before an error report is passed on to the underlying error reporter.
* <p>
* To cancel a report, return NULL.
* @param sender - the sender component.
* @param report - the error report.
* @param detailed - whether or not the report will be displayed in detail.
* @return The report to pass on, or NULL to cancel it.
*/
protected Report filterReport(Object sender, Report report, boolean detailed) {
return report;
}
@Override
public void reportWarning(Object sender, ReportBuilder reportBuilder) {
reportWarning(sender, reportBuilder.build());
}
@Override
public void reportDetailed(Object sender, ReportBuilder reportBuilder) {
reportDetailed(sender, reportBuilder.build());
}
}
package com.comphenix.protocol.error;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.error.Report.ReportBuilder;
/**
* Construct an error reporter that delegates to another error reporter.
* @author Kristian
*/
public class DelegatedErrorReporter implements ErrorReporter {
private final ErrorReporter delegated;
/**
* Construct a new error reporter that forwards all reports to a given reporter.
* @param delegated - the delegated reporter.
*/
public DelegatedErrorReporter(ErrorReporter delegated) {
this.delegated = delegated;
}
/**
* Retrieve the underlying error reporter.
* @return Underlying error reporter.
*/
public ErrorReporter getDelegated() {
return delegated;
}
@Override
public void reportMinimal(Plugin sender, String methodName, Throwable error) {
delegated.reportMinimal(sender, methodName, error);
}
@Override
public void reportMinimal(Plugin sender, String methodName, Throwable error, Object... parameters) {
delegated.reportMinimal(sender, methodName, error, parameters);
}
@Override
public void reportWarning(Object sender, Report report) {
Report transformed = filterReport(sender, report, false);
if (transformed != null) {
delegated.reportWarning(sender, transformed);
}
}
@Override
public void reportDetailed(Object sender, Report report) {
Report transformed = filterReport(sender, report, true);
if (transformed != null) {
delegated.reportDetailed(sender, transformed);
}
}
/**
* Invoked before an error report is passed on to the underlying error reporter.
* <p>
* To cancel a report, return NULL.
* @param sender - the sender instance or class.
* @param report - the error report.
* @param detailed - whether or not the report will be displayed in detail.
* @return The report to pass on, or NULL to cancel it.
*/
protected Report filterReport(Object sender, Report report, boolean detailed) {
return report;
}
@Override
public void reportWarning(Object sender, ReportBuilder reportBuilder) {
reportWarning(sender, reportBuilder.build());
}
@Override
public void reportDetailed(Object sender, ReportBuilder reportBuilder) {
reportDetailed(sender, reportBuilder.build());
}
}

Datei anzeigen

@ -1,499 +1,499 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.error;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.error.Report.ReportBuilder;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.reflect.PrettyPrinter;
import com.google.common.primitives.Primitives;
/**
* Internal class used to handle exceptions.
*
* @author Kristian
*/
public class DetailedErrorReporter implements ErrorReporter {
/**
* Report format for printing the current exception count.
*/
public static final ReportType REPORT_EXCEPTION_COUNT = new ReportType("Internal exception count: %s!");
public static final String SECOND_LEVEL_PREFIX = " ";
public static final String DEFAULT_PREFIX = " ";
public static final String DEFAULT_SUPPORT_URL = "http://dev.bukkit.org/server-mods/protocollib/";
// Users that are informed about errors in the chat
public static final String ERROR_PERMISSION = "protocol.info";
// We don't want to spam the server
public static final int DEFAULT_MAX_ERROR_COUNT = 20;
// Prevent spam per plugin too
private ConcurrentMap<String, AtomicInteger> warningCount = new ConcurrentHashMap<String, AtomicInteger>();
protected String prefix;
protected String supportURL;
protected AtomicInteger internalErrorCount = new AtomicInteger();
protected int maxErrorCount;
protected Logger logger;
protected WeakReference<Plugin> pluginReference;
protected String pluginName;
// Whether or not Apache Commons is not present
protected boolean apacheCommonsMissing;
// Map of global objects
protected Map<String, Object> globalParameters = new HashMap<String, Object>();
/**
* Create a default error reporting system.
*/
public DetailedErrorReporter(Plugin plugin) {
this(plugin, DEFAULT_PREFIX, DEFAULT_SUPPORT_URL);
}
/**
* Create a central error reporting system.
* @param plugin - the plugin owner.
* @param prefix - default line prefix.
* @param supportURL - URL to report the error.
*/
public DetailedErrorReporter(Plugin plugin, String prefix, String supportURL) {
this(plugin, prefix, supportURL, DEFAULT_MAX_ERROR_COUNT, getBukkitLogger());
}
/**
* Create a central error reporting system.
* @param plugin - the plugin owner.
* @param prefix - default line prefix.
* @param supportURL - URL to report the error.
* @param maxErrorCount - number of errors to print before giving up.
* @param logger - current logger.
*/
public DetailedErrorReporter(Plugin plugin, String prefix, String supportURL, int maxErrorCount, Logger logger) {
if (plugin == null)
throw new IllegalArgumentException("Plugin cannot be NULL.");
this.pluginReference = new WeakReference<Plugin>(plugin);
this.pluginName = plugin.getName();
this.prefix = prefix;
this.supportURL = supportURL;
this.maxErrorCount = maxErrorCount;
this.logger = logger;
}
// Attempt to get the logger.
private static Logger getBukkitLogger() {
try {
return Bukkit.getLogger();
} catch (Throwable e) {
return Logger.getLogger("Minecraft");
}
}
@Override
public void reportMinimal(Plugin sender, String methodName, Throwable error, Object... parameters) {
if (reportMinimalNoSpam(sender, methodName, error)) {
// Print parameters, if they are given
if (parameters != null && parameters.length > 0) {
logger.log(Level.SEVERE, printParameters(parameters));
}
}
}
@Override
public void reportMinimal(Plugin sender, String methodName, Throwable error) {
reportMinimalNoSpam(sender, methodName, error);
}
/**
* Report a problem with a given method and plugin, ensuring that we don't exceed the maximum number of error reports.
* @param sender - the component that observed this exception.
* @param methodName - the method name.
* @param error - the error itself.
* @return TRUE if the error was printed, FALSE if it was suppressed.
*/
public boolean reportMinimalNoSpam(Plugin sender, String methodName, Throwable error) {
String pluginName = PacketAdapter.getPluginName(sender);
AtomicInteger counter = warningCount.get(pluginName);
// Thread safe pattern
if (counter == null) {
AtomicInteger created = new AtomicInteger();
counter = warningCount.putIfAbsent(pluginName, created);
if (counter == null) {
counter = created;
}
}
final int errorCount = counter.incrementAndGet();
// See if we should print the full error
if (errorCount < getMaxErrorCount()) {
logger.log(Level.SEVERE, "[" + pluginName + "] Unhandled exception occured in " +
methodName + " for " + pluginName, error);
return true;
} else {
// Nope - only print the error count occationally
if (isPowerOfTwo(errorCount)) {
logger.log(Level.SEVERE, "[" + pluginName + "] Unhandled exception number " + errorCount + " occured in " +
methodName + " for " + pluginName, error);
}
return false;
}
}
/**
* Determine if a given number is a power of two.
* <p>
* That is, if there exists an N such that 2^N = number.
* @param number - the number to check.
* @return TRUE if the given number is a power of two, FALSE otherwise.
*/
private boolean isPowerOfTwo(int number) {
return (number & (number - 1)) == 0;
}
@Override
public void reportWarning(Object sender, ReportBuilder reportBuilder) {
if (reportBuilder == null)
throw new IllegalArgumentException("reportBuilder cannot be NULL.");
reportWarning(sender, reportBuilder.build());
}
@Override
public void reportWarning(Object sender, Report report) {
String message = "[" + pluginName + "] [" + getSenderName(sender) + "] " + report.getReportMessage();
// Print the main warning
if (report.getException() != null) {
logger.log(Level.WARNING, message, report.getException());
} else {
logger.log(Level.WARNING, message);
}
// Parameters?
if (report.hasCallerParameters()) {
// Write it
logger.log(Level.WARNING, printParameters(report.getCallerParameters()));
}
}
/**
* Retrieve the name of a sender class.
* @param sender - sender object.
* @return The name of the sender's class.
*/
private String getSenderName(Object sender) {
if (sender != null)
return sender.getClass().getSimpleName();
else
return "NULL";
}
@Override
public void reportDetailed(Object sender, ReportBuilder reportBuilder) {
reportDetailed(sender, reportBuilder.build());
}
@Override
public void reportDetailed(Object sender, Report report) {
final Plugin plugin = pluginReference.get();
final int errorCount = internalErrorCount.incrementAndGet();
// Do not overtly spam the server!
if (errorCount > getMaxErrorCount()) {
// Only allow the error count at rare occations
if (isPowerOfTwo(errorCount)) {
// Permit it - but print the number of exceptions first
reportWarning(this, Report.newBuilder(REPORT_EXCEPTION_COUNT).messageParam(errorCount).build());
} else {
// NEVER SPAM THE CONSOLE
return;
}
}
StringWriter text = new StringWriter();
PrintWriter writer = new PrintWriter(text);
// Helpful message
writer.println("[" + pluginName + "] INTERNAL ERROR: " + report.getReportMessage());
writer.println("If this problem hasn't already been reported, please open a ticket");
writer.println("at " + supportURL + " with the following data:");
// Now, let us print important exception information
writer.println(" ===== STACK TRACE =====");
if (report.getException() != null) {
report.getException().printStackTrace(writer);
}
// Data dump!
writer.println(" ===== DUMP =====");
// Relevant parameters
if (report.hasCallerParameters()) {
printParameters(writer, report.getCallerParameters());
}
// Global parameters
for (String param : globalParameters()) {
writer.println(SECOND_LEVEL_PREFIX + param + ":");
writer.println(addPrefix(getStringDescription(getGlobalParameter(param)),
SECOND_LEVEL_PREFIX + SECOND_LEVEL_PREFIX));
}
// Now, for the sender itself
writer.println("Sender:");
writer.println(addPrefix(getStringDescription(sender), SECOND_LEVEL_PREFIX));
// And plugin
if (plugin != null) {
writer.println("Version:");
writer.println(addPrefix(plugin.toString(), SECOND_LEVEL_PREFIX));
}
// Add the server version too
if (Bukkit.getServer() != null) {
writer.println("Server:");
writer.println(addPrefix(Bukkit.getServer().getVersion(), SECOND_LEVEL_PREFIX));
// Inform of this occurrence
if (ERROR_PERMISSION != null) {
Bukkit.getServer().broadcast(
String.format("Error %s (%s) occured in %s.", report.getReportMessage(), report.getException(), sender),
ERROR_PERMISSION
);
}
}
// Make sure it is reported
logger.severe(addPrefix(text.toString(), prefix));
}
private String printParameters(Object... parameters) {
StringWriter writer = new StringWriter();
// Print and retrieve the string buffer
printParameters(new PrintWriter(writer), parameters);
return writer.toString();
}
private void printParameters(PrintWriter writer, Object[] parameters) {
writer.println("Parameters: ");
// We *really* want to get as much information as possible
for (Object param : parameters) {
writer.println(addPrefix(getStringDescription(param), SECOND_LEVEL_PREFIX));
}
}
/**
* Adds the given prefix to every line in the text.
* @param text - text to modify.
* @param prefix - prefix added to every line in the text.
* @return The modified text.
*/
protected String addPrefix(String text, String prefix) {
return text.replaceAll("(?m)^", prefix);
}
/**
* Retrieve a string representation of the given object.
* @param value - object to convert.
* @return String representation.
*/
protected String getStringDescription(Object value) {
// We can't only rely on toString.
if (value == null) {
return "[NULL]";
} if (isSimpleType(value)) {
return value.toString();
} else {
try {
if (!apacheCommonsMissing)
return (ToStringBuilder.reflectionToString(value, ToStringStyle.MULTI_LINE_STYLE, false, null));
} catch (Throwable ex) {
// Apache is probably missing
apacheCommonsMissing = true;
}
// Use our custom object printer instead
try {
return PrettyPrinter.printObject(value, value.getClass(), Object.class);
} catch (IllegalAccessException e) {
return "[Error: " + e.getMessage() + "]";
}
}
}
/**
* Determine if the given object is a wrapper for a primitive/simple type or not.
* @param test - the object to test.
* @return TRUE if this object is simple enough to simply be printed, FALSE othewise.
*/
protected boolean isSimpleType(Object test) {
return test instanceof String || Primitives.isWrapperType(test.getClass());
}
/**
* Retrieve the current number of errors printed through {@link #reportDetailed(Object, Report)}.
* @return Number of errors printed.
*/
public int getErrorCount() {
return internalErrorCount.get();
}
/**
* Set the number of errors printed.
* @param errorCount - new number of errors printed.
*/
public void setErrorCount(int errorCount) {
internalErrorCount.set(errorCount);
}
/**
* Retrieve the maximum number of errors we can print before we begin suppressing errors.
* @return Maximum number of errors.
*/
public int getMaxErrorCount() {
return maxErrorCount;
}
/**
* Set the maximum number of errors we can print before we begin suppressing errors.
* @param maxErrorCount - new max count.
*/
public void setMaxErrorCount(int maxErrorCount) {
this.maxErrorCount = maxErrorCount;
}
/**
* Adds the given global parameter. It will be included in every error report.
* <p>
* Both key and value must be non-null.
* @param key - name of parameter.
* @param value - the global parameter itself.
*/
public void addGlobalParameter(String key, Object value) {
if (key == null)
throw new IllegalArgumentException("key cannot be NULL.");
if (value == null)
throw new IllegalArgumentException("value cannot be NULL.");
globalParameters.put(key, value);
}
/**
* Retrieve a global parameter by its key.
* @param key - key of the parameter to retrieve.
* @return The value of the global parameter, or NULL if not found.
*/
public Object getGlobalParameter(String key) {
if (key == null)
throw new IllegalArgumentException("key cannot be NULL.");
return globalParameters.get(key);
}
/**
* Reset all global parameters.
*/
public void clearGlobalParameters() {
globalParameters.clear();
}
/**
* Retrieve a set of every registered global parameter.
* @return Set of all registered global parameters.
*/
public Set<String> globalParameters() {
return globalParameters.keySet();
}
/**
* Retrieve the support URL that will be added to all detailed reports.
* @return Support URL.
*/
public String getSupportURL() {
return supportURL;
}
/**
* Set the support URL that will be added to all detailed reports.
* @param supportURL - the new support URL.
*/
public void setSupportURL(String supportURL) {
this.supportURL = supportURL;
}
/**
* Retrieve the prefix to apply to every line in the error reports.
* @return Error report prefix.
*/
public String getPrefix() {
return prefix;
}
/**
* Set the prefix to apply to every line in the error reports.
* @param prefix - new prefix.
*/
public void setPrefix(String prefix) {
this.prefix = prefix;
}
/**
* Retrieve the current logger that is used to print all reports.
* @return The current logger.
*/
public Logger getLogger() {
return logger;
}
/**
* Set the current logger that is used to print all reports.
* @param logger - new logger.
*/
public void setLogger(Logger logger) {
this.logger = logger;
}
}
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.error;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.error.Report.ReportBuilder;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.reflect.PrettyPrinter;
import com.google.common.primitives.Primitives;
/**
* Internal class used to handle exceptions.
*
* @author Kristian
*/
public class DetailedErrorReporter implements ErrorReporter {
/**
* Report format for printing the current exception count.
*/
public static final ReportType REPORT_EXCEPTION_COUNT = new ReportType("Internal exception count: %s!");
public static final String SECOND_LEVEL_PREFIX = " ";
public static final String DEFAULT_PREFIX = " ";
public static final String DEFAULT_SUPPORT_URL = "http://dev.bukkit.org/server-mods/protocollib/";
// Users that are informed about errors in the chat
public static final String ERROR_PERMISSION = "protocol.info";
// We don't want to spam the server
public static final int DEFAULT_MAX_ERROR_COUNT = 20;
// Prevent spam per plugin too
private ConcurrentMap<String, AtomicInteger> warningCount = new ConcurrentHashMap<String, AtomicInteger>();
protected String prefix;
protected String supportURL;
protected AtomicInteger internalErrorCount = new AtomicInteger();
protected int maxErrorCount;
protected Logger logger;
protected WeakReference<Plugin> pluginReference;
protected String pluginName;
// Whether or not Apache Commons is not present
protected boolean apacheCommonsMissing;
// Map of global objects
protected Map<String, Object> globalParameters = new HashMap<String, Object>();
/**
* Create a default error reporting system.
*/
public DetailedErrorReporter(Plugin plugin) {
this(plugin, DEFAULT_PREFIX, DEFAULT_SUPPORT_URL);
}
/**
* Create a central error reporting system.
* @param plugin - the plugin owner.
* @param prefix - default line prefix.
* @param supportURL - URL to report the error.
*/
public DetailedErrorReporter(Plugin plugin, String prefix, String supportURL) {
this(plugin, prefix, supportURL, DEFAULT_MAX_ERROR_COUNT, getBukkitLogger());
}
/**
* Create a central error reporting system.
* @param plugin - the plugin owner.
* @param prefix - default line prefix.
* @param supportURL - URL to report the error.
* @param maxErrorCount - number of errors to print before giving up.
* @param logger - current logger.
*/
public DetailedErrorReporter(Plugin plugin, String prefix, String supportURL, int maxErrorCount, Logger logger) {
if (plugin == null)
throw new IllegalArgumentException("Plugin cannot be NULL.");
this.pluginReference = new WeakReference<Plugin>(plugin);
this.pluginName = plugin.getName();
this.prefix = prefix;
this.supportURL = supportURL;
this.maxErrorCount = maxErrorCount;
this.logger = logger;
}
// Attempt to get the logger.
private static Logger getBukkitLogger() {
try {
return Bukkit.getLogger();
} catch (Throwable e) {
return Logger.getLogger("Minecraft");
}
}
@Override
public void reportMinimal(Plugin sender, String methodName, Throwable error, Object... parameters) {
if (reportMinimalNoSpam(sender, methodName, error)) {
// Print parameters, if they are given
if (parameters != null && parameters.length > 0) {
logger.log(Level.SEVERE, printParameters(parameters));
}
}
}
@Override
public void reportMinimal(Plugin sender, String methodName, Throwable error) {
reportMinimalNoSpam(sender, methodName, error);
}
/**
* Report a problem with a given method and plugin, ensuring that we don't exceed the maximum number of error reports.
* @param sender - the component that observed this exception.
* @param methodName - the method name.
* @param error - the error itself.
* @return TRUE if the error was printed, FALSE if it was suppressed.
*/
public boolean reportMinimalNoSpam(Plugin sender, String methodName, Throwable error) {
String pluginName = PacketAdapter.getPluginName(sender);
AtomicInteger counter = warningCount.get(pluginName);
// Thread safe pattern
if (counter == null) {
AtomicInteger created = new AtomicInteger();
counter = warningCount.putIfAbsent(pluginName, created);
if (counter == null) {
counter = created;
}
}
final int errorCount = counter.incrementAndGet();
// See if we should print the full error
if (errorCount < getMaxErrorCount()) {
logger.log(Level.SEVERE, "[" + pluginName + "] Unhandled exception occured in " +
methodName + " for " + pluginName, error);
return true;
} else {
// Nope - only print the error count occationally
if (isPowerOfTwo(errorCount)) {
logger.log(Level.SEVERE, "[" + pluginName + "] Unhandled exception number " + errorCount + " occured in " +
methodName + " for " + pluginName, error);
}
return false;
}
}
/**
* Determine if a given number is a power of two.
* <p>
* That is, if there exists an N such that 2^N = number.
* @param number - the number to check.
* @return TRUE if the given number is a power of two, FALSE otherwise.
*/
private boolean isPowerOfTwo(int number) {
return (number & (number - 1)) == 0;
}
@Override
public void reportWarning(Object sender, ReportBuilder reportBuilder) {
if (reportBuilder == null)
throw new IllegalArgumentException("reportBuilder cannot be NULL.");
reportWarning(sender, reportBuilder.build());
}
@Override
public void reportWarning(Object sender, Report report) {
String message = "[" + pluginName + "] [" + getSenderName(sender) + "] " + report.getReportMessage();
// Print the main warning
if (report.getException() != null) {
logger.log(Level.WARNING, message, report.getException());
} else {
logger.log(Level.WARNING, message);
}
// Parameters?
if (report.hasCallerParameters()) {
// Write it
logger.log(Level.WARNING, printParameters(report.getCallerParameters()));
}
}
/**
* Retrieve the name of a sender class.
* @param sender - sender object.
* @return The name of the sender's class.
*/
private String getSenderName(Object sender) {
if (sender != null)
return ReportType.getSenderClass(sender).getSimpleName();
else
return "NULL";
}
@Override
public void reportDetailed(Object sender, ReportBuilder reportBuilder) {
reportDetailed(sender, reportBuilder.build());
}
@Override
public void reportDetailed(Object sender, Report report) {
final Plugin plugin = pluginReference.get();
final int errorCount = internalErrorCount.incrementAndGet();
// Do not overtly spam the server!
if (errorCount > getMaxErrorCount()) {
// Only allow the error count at rare occations
if (isPowerOfTwo(errorCount)) {
// Permit it - but print the number of exceptions first
reportWarning(this, Report.newBuilder(REPORT_EXCEPTION_COUNT).messageParam(errorCount).build());
} else {
// NEVER SPAM THE CONSOLE
return;
}
}
StringWriter text = new StringWriter();
PrintWriter writer = new PrintWriter(text);
// Helpful message
writer.println("[" + pluginName + "] INTERNAL ERROR: " + report.getReportMessage());
writer.println("If this problem hasn't already been reported, please open a ticket");
writer.println("at " + supportURL + " with the following data:");
// Now, let us print important exception information
writer.println(" ===== STACK TRACE =====");
if (report.getException() != null) {
report.getException().printStackTrace(writer);
}
// Data dump!
writer.println(" ===== DUMP =====");
// Relevant parameters
if (report.hasCallerParameters()) {
printParameters(writer, report.getCallerParameters());
}
// Global parameters
for (String param : globalParameters()) {
writer.println(SECOND_LEVEL_PREFIX + param + ":");
writer.println(addPrefix(getStringDescription(getGlobalParameter(param)),
SECOND_LEVEL_PREFIX + SECOND_LEVEL_PREFIX));
}
// Now, for the sender itself
writer.println("Sender:");
writer.println(addPrefix(getStringDescription(sender), SECOND_LEVEL_PREFIX));
// And plugin
if (plugin != null) {
writer.println("Version:");
writer.println(addPrefix(plugin.toString(), SECOND_LEVEL_PREFIX));
}
// Add the server version too
if (Bukkit.getServer() != null) {
writer.println("Server:");
writer.println(addPrefix(Bukkit.getServer().getVersion(), SECOND_LEVEL_PREFIX));
// Inform of this occurrence
if (ERROR_PERMISSION != null) {
Bukkit.getServer().broadcast(
String.format("Error %s (%s) occured in %s.", report.getReportMessage(), report.getException(), sender),
ERROR_PERMISSION
);
}
}
// Make sure it is reported
logger.severe(addPrefix(text.toString(), prefix));
}
private String printParameters(Object... parameters) {
StringWriter writer = new StringWriter();
// Print and retrieve the string buffer
printParameters(new PrintWriter(writer), parameters);
return writer.toString();
}
private void printParameters(PrintWriter writer, Object[] parameters) {
writer.println("Parameters: ");
// We *really* want to get as much information as possible
for (Object param : parameters) {
writer.println(addPrefix(getStringDescription(param), SECOND_LEVEL_PREFIX));
}
}
/**
* Adds the given prefix to every line in the text.
* @param text - text to modify.
* @param prefix - prefix added to every line in the text.
* @return The modified text.
*/
protected String addPrefix(String text, String prefix) {
return text.replaceAll("(?m)^", prefix);
}
/**
* Retrieve a string representation of the given object.
* @param value - object to convert.
* @return String representation.
*/
protected String getStringDescription(Object value) {
// We can't only rely on toString.
if (value == null) {
return "[NULL]";
} if (isSimpleType(value) || value instanceof Class<?>) {
return value.toString();
} else {
try {
if (!apacheCommonsMissing)
return (ToStringBuilder.reflectionToString(value, ToStringStyle.MULTI_LINE_STYLE, false, null));
} catch (Throwable ex) {
// Apache is probably missing
apacheCommonsMissing = true;
}
// Use our custom object printer instead
try {
return PrettyPrinter.printObject(value, value.getClass(), Object.class);
} catch (IllegalAccessException e) {
return "[Error: " + e.getMessage() + "]";
}
}
}
/**
* Determine if the given object is a wrapper for a primitive/simple type or not.
* @param test - the object to test.
* @return TRUE if this object is simple enough to simply be printed, FALSE othewise.
*/
protected boolean isSimpleType(Object test) {
return test instanceof String || Primitives.isWrapperType(test.getClass());
}
/**
* Retrieve the current number of errors printed through {@link #reportDetailed(Object, Report)}.
* @return Number of errors printed.
*/
public int getErrorCount() {
return internalErrorCount.get();
}
/**
* Set the number of errors printed.
* @param errorCount - new number of errors printed.
*/
public void setErrorCount(int errorCount) {
internalErrorCount.set(errorCount);
}
/**
* Retrieve the maximum number of errors we can print before we begin suppressing errors.
* @return Maximum number of errors.
*/
public int getMaxErrorCount() {
return maxErrorCount;
}
/**
* Set the maximum number of errors we can print before we begin suppressing errors.
* @param maxErrorCount - new max count.
*/
public void setMaxErrorCount(int maxErrorCount) {
this.maxErrorCount = maxErrorCount;
}
/**
* Adds the given global parameter. It will be included in every error report.
* <p>
* Both key and value must be non-null.
* @param key - name of parameter.
* @param value - the global parameter itself.
*/
public void addGlobalParameter(String key, Object value) {
if (key == null)
throw new IllegalArgumentException("key cannot be NULL.");
if (value == null)
throw new IllegalArgumentException("value cannot be NULL.");
globalParameters.put(key, value);
}
/**
* Retrieve a global parameter by its key.
* @param key - key of the parameter to retrieve.
* @return The value of the global parameter, or NULL if not found.
*/
public Object getGlobalParameter(String key) {
if (key == null)
throw new IllegalArgumentException("key cannot be NULL.");
return globalParameters.get(key);
}
/**
* Reset all global parameters.
*/
public void clearGlobalParameters() {
globalParameters.clear();
}
/**
* Retrieve a set of every registered global parameter.
* @return Set of all registered global parameters.
*/
public Set<String> globalParameters() {
return globalParameters.keySet();
}
/**
* Retrieve the support URL that will be added to all detailed reports.
* @return Support URL.
*/
public String getSupportURL() {
return supportURL;
}
/**
* Set the support URL that will be added to all detailed reports.
* @param supportURL - the new support URL.
*/
public void setSupportURL(String supportURL) {
this.supportURL = supportURL;
}
/**
* Retrieve the prefix to apply to every line in the error reports.
* @return Error report prefix.
*/
public String getPrefix() {
return prefix;
}
/**
* Set the prefix to apply to every line in the error reports.
* @param prefix - new prefix.
*/
public void setPrefix(String prefix) {
this.prefix = prefix;
}
/**
* Retrieve the current logger that is used to print all reports.
* @return The current logger.
*/
public Logger getLogger() {
return logger;
}
/**
* Set the current logger that is used to print all reports.
* @param logger - new logger.
*/
public void setLogger(Logger logger) {
this.logger = logger;
}
}

Datei anzeigen

@ -46,6 +46,39 @@ public class ReportType {
return errorFormat;
}
/**
* Retrieve the class of the given sender.
* <p>
* If the sender is already a Class, we return it.
* @param sender - the sender to look up.
* @return The class of the sender.
*/
public static Class<?> getSenderClass(Object sender) {
if (sender == null)
throw new IllegalArgumentException("sender cannot be NUll.");
else if (sender instanceof Class<?>)
return (Class<?>) sender;
else
return sender.getClass();
}
/**
* Retrieve the full canonical name of a given report type.
* <p>
* Note that the sender may be a class (for static callers), in which
* case it will be used directly instead of its getClass() method.
* <p>
* It is thus not advisable for class classes to report reports.
* @param sender - the sender, or its class.
* @param type - the report type.
* @return The full canonical name.
*/
public static String getReportName(Object sender, ReportType type) {
if (sender == null)
throw new IllegalArgumentException("sender cannot be NUll.");
return getReportName(getSenderClass(sender), type);
}
/**
* Retrieve the full canonical name of a given report type.
* <p>
@ -54,7 +87,7 @@ public class ReportType {
* @param type - the report instance.
* @return The full canonical name.
*/
public static String getReportName(Class<?> sender, ReportType type) {
private static String getReportName(Class<?> sender, ReportType type) {
if (sender == null)
throw new IllegalArgumentException("sender cannot be NUll.");

Datei anzeigen

@ -362,6 +362,10 @@ public class DelayedPacketManager implements ProtocolManager, InternalManager {
return asyncManager;
}
/**
* Update the asynchronous manager. This must be set.
* @param asyncManager - the asynchronous manager.
*/
public void setAsynchronousManager(AsynchronousManager asyncManager) {
this.asyncManager = asyncManager;
}

Datei anzeigen

@ -268,6 +268,10 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
// We need to delay this until we know if Netty is enabled
final DelayedPacketManager delayed = new DelayedPacketManager(reporter);
// They must reference each other
delayed.setAsynchronousManager(asyncManager);
asyncManager.setManager(delayed);
Futures.addCallback(BukkitFutures.nextEvent(library, WorldInitEvent.class), new FutureCallback<WorldInitEvent>() {
@Override
public void onSuccess(WorldInitEvent event) {
@ -297,7 +301,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
@Override
public void onFailure(Throwable error) {
reporter.reportWarning(this, Report.newBuilder(REPORT_TEMPORARY_EVENT_ERROR).error(error));
reporter.reportWarning(PacketFilterManager.class, Report.newBuilder(REPORT_TEMPORARY_EVENT_ERROR).error(error));
}
});