/*
* Copyright 2004 - 2008 Christian Sprajc. All rights reserved.
*
* This file is part of PowerFolder.
*
* PowerFolder 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.
*
* PowerFolder 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 PowerFolder. If not, see <http://www.gnu.org/licenses/>.
*
* $Id$
*/
package de.dal33t.powerfolder.util;
import static de.dal33t.powerfolder.disk.FolderSettings.FOLDER_SETTINGS_ID;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import de.dal33t.powerfolder.Controller;
import de.dal33t.powerfolder.Member;
import de.dal33t.powerfolder.disk.Folder;
import de.dal33t.powerfolder.light.FileInfo;
import de.dal33t.powerfolder.light.MemberInfo;
import de.dal33t.powerfolder.message.Identity;
import de.dal33t.powerfolder.message.NodeInformation;
import de.dal33t.powerfolder.net.ConnectionHandler;
import de.dal33t.powerfolder.net.ConnectionQuality;
import de.dal33t.powerfolder.transfer.Download;
import de.dal33t.powerfolder.transfer.DownloadManager;
import de.dal33t.powerfolder.transfer.TransferManager;
import de.dal33t.powerfolder.transfer.Upload;
import de.dal33t.powerfolder.util.compare.FileInfoComparator;
import de.dal33t.powerfolder.util.compare.MemberComparator;
import de.dal33t.powerfolder.util.logging.LoggingManager;
/**
* Utility class with methods for debugging
*
* @author <a href="mailto:totmacher@powerfolder.com">Christian Sprajc </a>
* @version $Revision: 1.30 $
*/
public class Debug {
private static final Logger log = Logger.getLogger(Debug.class.getName());
private static final MyThreadLocal DATE_FORMAT = new MyThreadLocal();
// private static Map<File, Collection<Object>> fileWatch = new
// HashMap<File, Collection<Object>>();
private Debug() {
// No instance allowed
}
public static long countDataitems(Controller controller) {
long dbSize = 0;
for (Folder folder : controller.getFolderRepository().getFolders(true))
{
for (Member member : folder.getMembersAsCollection()) {
dbSize += folder.getDAO().count(member.getId(), true, false);
}
}
return dbSize;
}
/**
* Dumps the system properties into the debug directory.
*/
public static void writeSystemProperties() {
if (!LoggingManager.isLogToFile()) {
return;
}
File file = new File(LoggingManager.getDebugDir(),
"system_properties.txt");
try {
Properties sysprops = System.getProperties();
PropertiesUtil.saveConfig(file, sysprops, "Current time: "
+ new Date());
} catch (FileNotFoundException e) {
log.severe("Unable to create SystemInfo file");
} catch (IOException e) {
log.severe("Unable to Write to '" + file + '\'');
}
}
/**
* Writes a list of files to disk as CSV file.
*
* @param folderName
* @param memberName
* @param fileInfos
* @param header
* @return the CSV file. Or null if failed
*/
public static File writeFileListCSV(String folderName, String memberName,
Collection<FileInfo> fileInfos, String header)
{
Reject.ifBlank(folderName, "folderName is null");
Reject.ifBlank(memberName, "memberName is null");
Reject.ifNull(fileInfos, "Files are null");
File filelistsDir = new File(LoggingManager.getDebugDir(), "filelists");
filelistsDir.mkdirs();
File logFile = new File(filelistsDir,
FileUtils.removeInvalidFilenameChars(folderName) + File.separator
+ FileUtils.removeInvalidFilenameChars(memberName)
+ ".list.txt");
return writeFileListCSV(logFile, fileInfos, header);
}
/**
* Writes a list of files to disk as CSV file.
*
* @param logFile
* @param fileInfos
* @param header
* @return the CSV file or null if failed.
*/
public static File writeFileListCSV(File logFile,
Collection<FileInfo> fileInfos, String header)
{
if (!logFile.exists()) {
try {
if (logFile.getParentFile() != null) {
logFile.getParentFile().mkdirs();
}
logFile.createNewFile();
} catch (IOException e) {
log.severe("Unable to write filelist to "
+ logFile.getAbsolutePath());
log.log(Level.FINER, "IOException", e);
return null;
}
}
if (!logFile.canWrite()) {
log.severe("Unable to write filelist to "
+ logFile.getAbsolutePath());
return null;
}
// Copy & Sort
FileInfo[] list = fileInfos.toArray(new FileInfo[fileInfos.size()]);
Arrays.sort(list, new FileInfoComparator(
FileInfoComparator.BY_MODIFIED_DATE));
try {
OutputStream fOut = new BufferedOutputStream(new FileOutputStream(
logFile));
fOut.write(("# " + header + "\n\n").getBytes("UTF-8"));
fOut.write("Change time ;Filename;Changer;Size;Version\n\n"
.getBytes());
for (FileInfo aList : list) {
fOut.write(toCSVLine(aList).getBytes("UTF-8"));
}
fOut.close();
return logFile;
} catch (IOException e) {
log.severe("Unable to write nodelist to '"
+ logFile.getAbsolutePath() + '\'');
log.log(Level.FINER, "IOException", e);
}
return null;
}
/**
* Details infos about the fileinfo to a comma separated line.
*
* @param f
*/
private static String toCSVLine(FileInfo f) {
Reject.ifNull(f, "FileInfo is null");
StringBuilder b = new StringBuilder();
b.append(f.getModifiedDate() != null ? DATE_FORMAT.get().format(
f.getModifiedDate()) : "-");
b.append(" ;");
if (f.isDeleted()) {
b.append("(del) ");
}
b.append(f.getRelativeName());
b.append(f.isDiretory() ? " (D)" : "");
b.append(';');
b.append(f.getModifiedBy().nick);
b.append(';');
b.append(Format.formatBytes(f.getSize()));
b.append(';');
b.append(f.getVersion());
b.append('\n');
return b.toString();
}
/**
* Builds a debug report for remote analyse
*
* @param c
* @return
*/
public static String buildDebugReport(Controller c) {
if (c == null) {
throw new NullPointerException("Controller is null");
}
synchronized (c) {
StringBuffer b = new StringBuffer();
b.append("PowerFolder debug report\n");
b.append("------------------------");
// information about myself and local port binding
long uptimeMinutes = c.getUptime() / 1000 / 60;
b.append("\nVersion: " + Controller.PROGRAM_VERSION + " ("
+ c.getBuildTime() + ')');
b.append("\nConfig: " + c.getConfigName());
b.append("\nCurrent time: " + new Date());
b.append("\nLocale: " + Locale.getDefault() + " ("
+ Locale.getDefault().getDisplayCountry() + ')');
b.append("\nUptime: " + uptimeMinutes + " minutes");
b.append("\nOS: " + System.getProperty("os.name"));
b.append("\nJava: " + JavaVersion.systemVersion().toString() + " ("
+ System.getProperty("java.vendor") + ')');
b.append("\nNetworking mode: ");
b.append(c.getNetworkingMode().name());
double upKBS = c.getTransferManager()
.getTotalUploadTrafficCounter().calculateCurrentKBS();
double downKBS = c.getTransferManager()
.getTotalDownloadTrafficCounter().calculateCurrentKBS();
long upBytes = c.getTransferManager()
.getTotalUploadTrafficCounter().getBytesTransferred();
long downBytes = c.getTransferManager()
.getTotalDownloadTrafficCounter().getBytesTransferred();
b.append("\nTotal traffic: DOWN " + Format.formatDecimal(downKBS)
+ " Kbytes/s, " + Format.formatBytes(downBytes)
+ " bytes total, UP " + Format.formatDecimal(upKBS)
+ " Kbytes/s, " + Format.formatBytes(upBytes) + " bytes total");
if (c.isLimitedConnectivity()) {
b.append("\nWARNING: Has limited connectivity");
}
b.append("\n\nListener status: ");
if (c.hasConnectionListener()) {
b.append("Listening on ");
b.append(c.getConnectionListener().getAddress());
if (c.getMySelf() != null
&& c.getMySelf().getInfo().isSupernode)
{
b.append(", acting as supernode");
} else {
b.append(", acting as standardnode");
}
b.append('\n');
} else {
b.append("Not listening on a local port\n");
}
b.append("MySelf: ");
addDetailInfo(b, c.getMySelf());
b.append('\n');
if (c.isStarted()) {
// All folders
Collection<Folder> folders = c.getFolderRepository()
.getFolders(true);
b.append("\nFolders (" + folders.size() + " joined)");
for (Folder folder : folders) {
b.append("\n ");
addDetailInfo(b, folder);
}
b.append('\n');
if (folders.isEmpty()) {
b.append(" (none)\n");
}
TransferManager tm = c.getTransferManager();
// dump transfers
Collection<DownloadManager> downloads = c.getTransferManager()
.getActiveDownloads();
b.append("\nDownloads ("
+ downloads.size()
+ " total, "
+ Format.formatDecimal(tm.getDownloadCounter()
.calculateCurrentKBS())
+ " Kbytes/s, "
+ Format.formatBytes(tm.getDownloadCounter()
.getBytesTransferred()) + " bytes total):");
for (DownloadManager man : downloads) {
for (Download dl : man.getSources()) {
b.append("\n ");
b.append(dl.isStarted() ? "(active)" : (dl.isQueued()
? "(queued)"
: "(requested)"));
b.append(" " + dl);
}
}
b.append('\n');
if (downloads.isEmpty()) {
b.append(" (none)\n");
}
b.append("\nUploads ("
+ tm.countActiveUploads()
+ " active, "
+ tm.countQueuedUploads()
+ " queued, "
+ Format.formatDecimal(tm.getUploadCounter()
.calculateCurrentKBS())
+ " Kbytes/s, "
+ Format.formatDecimal(tm.getUploadCPSForWAN() / 1024)
+ " Kbyte/s allowed, "
+ Format.formatBytes(tm.getUploadCounter()
.getBytesTransferred()) + " bytes total):");
List<Upload> uploads = new ArrayList<Upload>();
uploads.addAll(tm.getActiveUploads());
uploads.addAll(tm.getQueuedUploads());
for (Object upload1 : uploads) {
Upload upload = (Upload) upload1;
b.append("\n ");
b.append(upload.isStarted() ? "(active)" : "(queued)");
b.append(" " + upload);
}
b.append('\n');
if (uploads.isEmpty()) {
b.append(" (none)\n");
}
// all members
Member[] knownMembers = c
.getNodeManager()
.getNodesAsCollection()
.toArray(
new Member[c.getNodeManager().getNodesAsCollection()
.size()]);
// Sort
Arrays.sort(knownMembers, MemberComparator.IN_GUI);
b.append("\nAll online nodes ("
+ c.getNodeManager().countConnectedNodes() + " connected, "
+ c.getNodeManager().countOnlineNodes() + " online, "
+ c.getNodeManager().getNodesAsCollection().size()
+ " known, " + c.getNodeManager().countSupernodes()
+ " supernodes, " + c.getNodeManager().countFriends()
+ " friend(s)):");
for (Member knownMember : knownMembers) {
if (knownMember.isConnectedToNetwork()) {
b.append("\n ");
addDetailInfo(b, knownMember);
}
}
b.append('\n');
if (knownMembers.length == 0) {
b.append(" (none)\n");
}
} else {
b.append("Controller NOT started yet\n");
}
// config dump, even if controller is not started yet
b.append("\nConfig:");
Properties config = (Properties) c.getConfig().clone();
// Sort config by name
List sortedConfigKeys = new ArrayList(config.keySet());
Collections.sort(sortedConfigKeys);
for (Object sortedConfigKey : sortedConfigKeys) {
String key = (String) sortedConfigKey;
String value = config.getProperty(key);
// Erase folder ids, keep it secret!
if (key.indexOf(FOLDER_SETTINGS_ID) >= 5) {
value = "XXX-erased-XXX";
}
// Erase all passwords
if (key.toLowerCase().contains("password")) {
value = "XXX-erased-XXX";
}
if (key.toLowerCase().contains("license")) {
value = "XXX-erased-XXX";
}
b.append("\n " + key + " = " + value);
}
b.append('\n');
/*
* b.append("\nFolder details:"); for (int i = 0; i <
* folders.length; i++) { b.append("\n "); addFullInfo(b,
* c.getFolderRepository().getFolder(folders[i])); } b.append("\n");
*/
return b.toString();
}
}
/**
* Adds a detailed info about the member to the buffer
*
* @param b
* @param m
*/
private static void addDetailInfo(StringBuffer b, Member m) {
if (b == null || m == null) {
return;
}
b.append(toDetailInfo(m));
}
/**
* Details infos about the member.
*
* @param m
*/
private static String toDetailInfo(Member m) {
Reject.ifNull(m, "Member is null");
StringBuilder b = new StringBuilder();
if (m.isMySelf()) {
b.append("(mee) ");
} else if (m.isConnected()) {
if (m.isOnLAN()) {
b.append("(LAN) ");
} else {
ConnectionHandler peer = m.getPeer();
if (peer != null) {
ConnectionQuality q = peer.getConnectionQuality();
if (q.equals(ConnectionQuality.GOOD)) {
b.append("(***) ");
} else if (q.equals(ConnectionQuality.MEDIUM)) {
b.append("(** ) ");
} else {
b.append("(* ) ");
}
} else {
b.append("(???) ");
}
}
} else if (m.isConnectedToNetwork()) {
b.append("(on ) ");
} else {
b.append("(off) ");
}
if (m.getInfo().isSupernode) {
b.append("(s) ");
}
b.append(m);
Identity id = m.getIdentity();
b.append(", ver. " + (id != null ? id.getProgramVersion() : "-")
+ ", ID: " + m.getId());
b.append(", reconnect address " + m.getReconnectAddress());
return b.toString();
}
/**
* Details infos about the member ad a comma separated line.
*
* @param m
*/
private static String toCSVLine(Member m) {
Reject.ifNull(m, "Member is null");
StringBuilder b = new StringBuilder();
if (m.isMySelf()) {
b.append("myself");
} else if (m.isConnected()) {
if (m.isOnLAN()) {
b.append("connected (local)");
} else {
b.append("connected (i-net)");
}
} else if (m.isConnectedToNetwork()) {
b.append("online");
} else {
b.append("offline");
}
b.append(';');
if (m.getInfo().isSupernode) {
b.append('s');
} else {
b.append('n');
}
b.append(';');
b.append(m.getNick());
b.append(';' + m.getId());
b.append(';');
Identity id = m.getIdentity();
b.append(id != null ? id.getProgramVersion() : "-");
b.append(";" + m.getReconnectAddress());
b.append(";" + m.getLastConnectTime());
b.append(";" + m.getLastNetworkConnectTime());
return b.toString();
}
/**
* Adds detailed info about the folder to buffer
*
* @param b
* @param f
*/
private static void addDetailInfo(StringBuffer b, Folder f) {
if (b == null || f == null) {
return;
}
b.append(f);
b.append(", ID: XXX-erased-XXX");
b.append(", files: " + f.getKnownItemCount() + ", size: "
+ Format.formatBytes(f.getStatistic().getLocalSize())
+ ", members: " + f.getMembersCount() + ", mode: "
+ f.getSyncProfile().getName() + ", sync: "
+ Format.formatPercent(+f.getStatistic().getLocalSyncPercentage()));
}
/**
* Writes debug report to disk.
*
* @see #loadDebugReport(MemberInfo)
* @param nodeInfo
* @return if succeeded
*/
public static boolean writeNodeInformation(NodeInformation nodeInfo) {
if (nodeInfo == null) {
throw new NullPointerException("NodeInfo is null");
}
String fileName;
if (nodeInfo.node != null) {
fileName = FileUtils.removeInvalidFilenameChars(nodeInfo.node.nick)
+ ".report.txt";
} else {
fileName = "-unknown-.report.txt";
}
try {
// Create in debug directory
// Create dir
File dir = new File(LoggingManager.getDebugDir(), "nodeinfos");
dir.mkdirs();
OutputStream fOut = new BufferedOutputStream(new FileOutputStream(
new File(dir, fileName)));
fOut.write(nodeInfo.debugReport.getBytes());
fOut.close();
return true;
} catch (IOException e) {
log.log(Level.FINER, "IOException", e);
}
return false;
}
/**
* Loads a stored debug report from disk for that node
*
* @see #writeNodeInformation(NodeInformation)
* @param node
* @return
*/
public static String loadDebugReport(MemberInfo node) {
Reject.ifNull(node, "Node is null");
String fileName = "Node." + node.nick + ".report.txt";
try {
File file = new File(LoggingManager.getDebugDir(), "nodeinfos/"
+ fileName);
InputStream fIn = new BufferedInputStream(new FileInputStream(file));
byte[] buffer = new byte[(int) file.length()];
fIn.read(buffer);
return new String(buffer);
} catch (IOException e) {
log.warning("Debug report for " + node.nick + " not found ("
+ fileName + ')');
// Loggable.logFinerStatic(Debug.class, e);
}
return null;
}
/**
* Writes a list of nodes to a debut output file.
*
* @param nodes
* the list of nodes
* @param fileName
* the filename to write to
*/
public static void writeNodeList(Collection<Member> nodes, String fileName)
{
Reject.ifNull(nodes, "Nodelist is null");
try {
OutputStream fOut = new BufferedOutputStream(new FileOutputStream(
new File(LoggingManager.getDebugDir(), fileName)));
for (Member node : nodes) {
fOut.write(toDetailInfo(node).getBytes());
fOut.write("\n".getBytes());
}
fOut.close();
} catch (IOException e) {
log.warning("Unable to write nodelist to '" + fileName + '\'');
log.log(Level.FINER, "IOException", e);
}
}
/**
* Writes a list of nodes to a debut output file in csv format.
*
* @param nodes
* the list of nodes
* @param fileName
* the filename to write to
*/
public static void writeNodeListCSV(Collection<Member> nodes,
String fileName)
{
Reject.ifNull(nodes, "Nodelist is null");
try {
OutputStream fOut = new BufferedOutputStream(new FileOutputStream(
new File(LoggingManager.getDebugDir(), fileName)));
fOut.write("connect;supernode;nick;id;version;address;last connect time;last online time\n"
.getBytes());
synchronized (nodes) {
for (Member node : nodes) {
fOut.write(toCSVLine(node).getBytes());
fOut.write("\n".getBytes());
}
}
fOut.close();
} catch (IOException e) {
log.warning("Unable to write nodelist to '" + fileName + '\'');
log.log(Level.FINER, "IOException", e);
}
}
/**
* Writes statistics to disk
*
* @param controller
*/
public static void writeStatistics(Controller controller) {
OutputStream fOut = null;
try {
File file = new File(LoggingManager.getDebugDir(),
controller.getConfigName() + ".netstat.csv");
file.getParentFile().mkdirs();
fOut = new BufferedOutputStream(new FileOutputStream(file, true));
Date now = new Date();
String statLine = Format.formatDateShort(now) + ';' + now.getTime()
+ ';' + controller.getNodeManager().countConnectedNodes() + ';'
+ controller.getNodeManager().countOnlineNodes() + ';'
+ controller.getNodeManager().getNodesAsCollection().size()
+ '\n';
fOut.write(statLine.getBytes());
} catch (IOException e) {
log.log(Level.WARNING, "Unable to write network statistics file", e);
// Ignore
} finally {
try {
if (fOut != null) {
fOut.close();
}
} catch (Exception e) {
// ignore
}
}
}
// public static void openedFile(File f, Object src) {
// synchronized (fileWatch) {
// Collection<Object> o = fileWatch.get(f);
// if (o == null) {
// o = new HashSet<Object>();
// fileWatch.put(f, o);
// }
// o.add(src);
// }
// }
//
// public static void closedFile(File f, Object src) {
// synchronized (fileWatch) {
// Collection<Object> o = fileWatch.get(f);
// if (o == null) {
// throw new IllegalStateException("File isn't open!");
// }
// if (!o.remove(src)) {
// throw new IllegalStateException("File isn't open by " + src);
// }
// }
// }
//
// public static void dumpOpeners(File f) {
// synchronized (fileWatch) {
// Collection<Object> o = fileWatch.get(f);
// if (o != null) {
// for (Object s : o) {
// Loggable.logWarningStatic(Debug.class, f + " opened by "
// + s);
// }
// } else {
// Loggable.logWarningStatic(Debug.class, f + " is not open!");
// }
// }
// System.exit(1);
// }
//
// public static void dumpOpenFiles() {
// synchronized (fileWatch) {
// for (File f : fileWatch.keySet()) {
// dumpOpeners(f);
// }
// }
// }
public static void dumpThreadStacks() {
ThreadGroup top = Thread.currentThread().getThreadGroup();
while (top.getParent() != null) {
top = top.getParent();
}
showGroupInfo(top);
}
public static String getStackTrace(StackTraceElement[] stack) {
StringBuilder b = new StringBuilder();
for (StackTraceElement e : stack) {
b.append(e.toString()).append('\n');
}
return b.toString();
}
public static void dumpCurrentStackTrace() {
log.fine(getCurrentStackTrace());
}
public static String getCurrentStackTrace() {
return getStackTrace(Thread.currentThread().getStackTrace());
}
public static String dumpCurrentStacktraces(boolean hideIdleThreds) {
ThreadGroup top = Thread.currentThread().getThreadGroup();
while (top.getParent() != null) {
top = top.getParent();
}
StringBuilder b = new StringBuilder();
for (String dumps : getGroupInfo(top, hideIdleThreds)) {
b.append(dumps);
}
return b.toString();
}
private static List<String> getGroupInfo(ThreadGroup group,
boolean hideIdleThreds)
{
Thread threads[] = new Thread[group.activeCount()];
List<String> threadDumps = new LinkedList<String>();
group.enumerate(threads, false);
for (Thread thread : threads) {
if (thread != null) {
String threadDump = dumpStackTrace(thread, hideIdleThreds);
if (StringUtils.isBlank(threadDump)) {
continue;
}
String dump = " " + thread
+ " --------------------------------------\n";
dump += threadDump;
dump += "\n";
threadDumps.add(dump);
}
}
ThreadGroup[] activeGroup = new ThreadGroup[group.activeGroupCount()];
group.enumerate(activeGroup, false);
int i = 0;
while (i < activeGroup.length) {
threadDumps.addAll(getGroupInfo(activeGroup[i], hideIdleThreds));
i++;
}
return threadDumps;
}
private static String dumpStackTrace(Thread t, boolean hideIdleThreds) {
StringBuilder b = new StringBuilder();
for (StackTraceElement te : t.getStackTrace()) {
if (hideIdleThreds) {
if (te.toString().contains(
"java.net.SocketInputStream.socketRead0"))
{
return null;
}
if (te.toString().contains("java.lang.Thread.sleep")) {
return null;
}
if (te.toString().contains("java.lang.Object.wait")) {
return null;
}
if (te.toString()
.contains("sun.awt.windows.WToolkit.eventLoop"))
{
return null;
}
if (te.toString().contains("sun.misc.Unsafe.park")) {
return null;
}
if (te.toString().contains(
"java.net.PlainSocketImpl.socketAccept"))
{
return null;
}
if (te.toString().contains(
"java.net.SocketOutputStream.socketWrite0(Native Method)"))
{
return null;
}
if (te.toString().contains(
"java.net.PlainDatagramSocketImpl.receive0"))
{
return null;
}
if (te.toString().contains("java.lang.Thread.getStackTrace")) {
return null;
}
if (te.toString().contains(
"java.net.PlainSocketImpl.socketConnect"))
{
return null;
}
if (te
.toString()
.contains(
"net.contentobjects.jnotify.linux.JNotify_linux.nativeNotifyLoop"))
{
return null;
}
if (te.toString().contains(
"de.dal33t.powerfolder.util.net.UDTSocket.recv"))
{
return null;
}
}
b.append(" " + te);
b.append("\n");
}
return b.toString();
}
private static String detailedObjectState0(Class<?> c, Object o) {
if (c == Object.class) {
return "";
}
StringBuilder buffer = new StringBuilder();
buffer.append(detailedObjectState0(c.getSuperclass(), o));
Field[] fields = c.getDeclaredFields();
for (Field fld : fields) {
fld.setAccessible(true);
buffer.append("; [").append("Field: ").append(fld.getName());
buffer.append(", toString: ");
try {
Object value = fld.get(o);
buffer.append('(').append(value).append(')');
} catch (IllegalArgumentException e) {
buffer.append(e);
} catch (IllegalAccessException e) {
buffer.append(e);
}
buffer.append(']');
fld.setAccessible(false);
}
return buffer.toString();
}
public static String detailedObjectState(Object o) {
StringBuilder buffer = new StringBuilder();
buffer.append("Class: ").append(o.getClass().getName());
buffer.append(detailedObjectState0(o.getClass(), o));
return buffer.toString();
}
private static void showGroupInfo(ThreadGroup group) {
Thread threads[] = new Thread[group.activeCount()];
group.enumerate(threads, false);
log.fine("");
log.fine(group + " ########################");
for (Thread thread : threads) {
if (thread != null) {
log.fine(" " + thread
+ " --------------------------------------");
dumpStackTrace(thread);
log.fine("");
}
}
ThreadGroup[] activeGroup = new ThreadGroup[group.activeGroupCount()];
group.enumerate(activeGroup, false);
int i = 0;
while (i < activeGroup.length) {
showGroupInfo(activeGroup[i]);
i++;
}
}
private static void dumpStackTrace(Thread t) {
for (StackTraceElement te : t.getStackTrace()) {
log.fine(" " + te);
}
}
/**
* ThreadLocal date formatter.
*/
private static class MyThreadLocal extends ThreadLocal<DateFormat> {
protected DateFormat initialValue() {
return new SimpleDateFormat("dd-MM-yyyy HH:mm");
}
}
}