package net.sf.colossus.util; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import java.util.logging.Level; import java.util.logging.Logger; /** * Objects can register here when they are created. * * This class notices when it an object is garbage collected * and keeps then track which instances are still running/alive. * * One can configure for which classes one is interested * in seeing the created/removed delta. * * This is meant for debug/development purposes, to verify * that cleanup is usually done properly, and not threads * or objects stay unintentionally referenced and never * get garbage collected. * * So while editing/testing, one sets the "which one to see" * to the ones one want to monitor, but in productive use, * i.e. when checked in to svn, this should be set to * "don't show anything". * (perhaps with Java 1.5 or 1.6, or a good debugger one can * achieve the same effect, but I don't know how ;-) * * @author Clemens Katzer */ public class InstanceTracker { private static final Logger LOGGER = Logger .getLogger(InstanceTracker.class.getName()); private static Map<String, InstanceGroup> instanceGroups = new WeakHashMap<String, InstanceGroup>(); private static Set<String> interestedIn = new HashSet<String>(); // private static String prefix = "net.sf.colossus."; private static boolean interestedInAll = true; private static boolean ignoreDummyFrame = true; // if interestedInAll is false, then it registers only those // put into the HashSet with the block below: static { interestedIn.add("net.sf.colossus.client.Client"); interestedIn.add("net.sf.colossus.client.SocketClientThread"); interestedIn.add("net.sf.colossus.client.MasterBoard"); interestedIn.add("net.sf.colossus.ai.SimpleAI"); interestedIn.add("net.sf.colossus.util.KFrame"); interestedIn.add("net.sf.colossus.util.KDialog"); interestedIn.add("net.sf.colossus.client.BattleMap"); interestedIn.add("net.sf.colossus.client.ShowReadme"); interestedIn.add("net.sf.colossus.client.ShowHelpDoc"); interestedIn.add("net.sf.colossus.server.PlayerServerSide"); interestedIn.add("net.sf.colossus.server.Start"); interestedIn.add("net.sf.colossus.server.GetPlayers"); interestedIn.add("net.sf.colossus.server.GameServerSide"); interestedIn.add("net.sf.colossus.server.Server"); interestedIn.add("net.sf.colossus.server.ClientHandler"); interestedIn.add("net.sf.colossus.util.KDialog"); interestedIn.add("net.sf.colossus.client.StatusScreen"); interestedIn.add("net.sf.colossus.client.WebClient"); } public static synchronized void register(Object o, String id) { String type = o.getClass().getName(); if ((interestedIn.contains(type) || interestedInAll) && (ignoreDummyFrame || !type .equals("net.sf.colossus.util.DummyFrameWithMenu"))) { LOGGER.log(Level.FINEST, "Registering object of type " + type + " with id " + id); if (instanceGroups.containsKey(type)) { LOGGER.log(Level.FINEST, "Adding to existing group " + type); InstanceGroup group = instanceGroups.get(type); group.addInstance(o, id); } else { LOGGER.log(Level.FINEST, "Creating new group for " + type); InstanceGroup group = new InstanceGroup(type); group.addInstance(o, id); instanceGroups.put(type, group); } } else { LOGGER.log(Level.FINEST, "NOT registering object of type " + type + " with id " + id); } } public static synchronized void setId(Object o, String id) { String type = o.getClass().getName(); String shortType = InstanceGroup.shortType(type); if (interestedIn.contains(type)) { LOGGER.log(Level.FINEST, "InstanceTracker.setId(): One object of type " + shortType + " changes ID to '" + id + "'"); } // nothing needs to be actually done, it will disappear by itself // from the WeakHashMap... if (instanceGroups.containsKey(type)) { InstanceGroup group = instanceGroups.get(type); InstanceGroup.TypeInstance i = group.getInstance(o); if (i != null) { i.setId(id); } } } public static synchronized void printStatistics() { String stat = getPrintStatistics(); LOGGER.log(Level.INFO, stat); } private static synchronized String getPrintStatistics() { StringBuilder stat = new StringBuilder(); stat.append("==========\nObject instances statistics:"); Iterator<String> it = instanceGroups.keySet().iterator(); while (it.hasNext()) { String type = it.next(); InstanceGroup group = instanceGroups.get(type); stat.append(group.getPrintStatistics()); } stat.append("\n"); return stat.substring(0); } public static synchronized boolean allGone() { Iterator<String> it = instanceGroups.keySet().iterator(); while (it.hasNext()) { String type = it.next(); InstanceGroup group = instanceGroups.get(type); if (group.amountLeft() != 0) { return false; } } return true; } }