/*
* RHQ Management Platform
* Copyright (C) 2005-2011 Red Hat, Inc.
* All rights reserved.
*
* 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 version 2 of the License.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.plugins.apache.augeas;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jboss.byteman.rule.Rule;
import org.jboss.byteman.rule.helper.Helper;
/**
* This is a helper class to help us make sense of the augeas leaks.
*
* The byteman rules in the {@link AugeasReferenceLeakingTest} make use
* of this class to record the locations that create instances of augeas
* and match them with locations that close the instance.
* <p>
* We can then report on any locations that created an augeas instance
* that was never closed.
*
* @author Lukas Krejci
*/
public class CreateAndCloseTracker extends Helper {
private static class EqualableWeakReference<T> extends WeakReference<T> {
private final int hash;
public EqualableWeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
hash = referent.hashCode();
}
public EqualableWeakReference(T referent) {
super(referent);
hash = referent.hashCode();
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof EqualableWeakReference)) {
return false;
}
Object ref = get();
if (ref == null) {
//the reference has been cleared. There is no telling
//on what this reference pointed to, so it is not
//possible to determine equality.
return false;
}
EqualableWeakReference<?> o = (EqualableWeakReference<?>) other;
Object oref = o.get();
//reference equality is what we're after here...
return ref == oref;
}
}
private static class CreateAndCloseLocations {
public String createLocation;
public List<String> closeLocations = new ArrayList<String>();
}
private static final HashMap<EqualableWeakReference<Object>, CreateAndCloseLocations> REFERENCES = new HashMap<CreateAndCloseTracker.EqualableWeakReference<Object>, CreateAndCloseLocations>();
/**
* @param rule
*/
public CreateAndCloseTracker(Rule rule) {
super(rule);
}
private static synchronized void _recordCreate(Object object, String stackTrace) {
EqualableWeakReference<Object> ref = new EqualableWeakReference<Object>(object);
CreateAndCloseLocations locations = REFERENCES.get(ref);
if (locations == null) {
locations = new CreateAndCloseLocations();
REFERENCES.put(ref, locations);
locations.createLocation = stackTrace;
} else {
throw new IllegalStateException("Cannot record a create request on a single object twice. Object: " + object + ", Stacktrace: " + stackTrace);
}
}
public void recordCreate(Object object, String stackTrace) {
_recordCreate(object, stackTrace);
}
private static synchronized void _recordClose(Object object, String stackTrace) {
EqualableWeakReference<Object> ref = new EqualableWeakReference<Object>(object);
CreateAndCloseLocations locations = REFERENCES.get(ref);
if (locations == null) {
//weird, but actually track this so that we
//can report about an object not being
//recorded for create.
locations = new CreateAndCloseLocations();
REFERENCES.put(ref, locations);
}
locations.closeLocations.add(stackTrace);
}
public void recordClose(Object object, String stackTrace) {
_recordClose(object, stackTrace);
}
public static synchronized void clear() {
REFERENCES.clear();
}
public static synchronized List<String> getCreateLocationsWithoutClose() {
ArrayList<String> ret = new ArrayList<String>();
for(CreateAndCloseLocations l : REFERENCES.values()) {
if (l.closeLocations.isEmpty()) {
ret.add(l.createLocation);
}
}
return ret;
}
public static synchronized Map<String, List<String>> getMultiplyClosingLocations() {
HashMap<String, List<String>> ret = new HashMap<String, List<String>>();
for(CreateAndCloseLocations l : REFERENCES.values()) {
if (l.closeLocations.size() > 1) {
ret.put(l.createLocation, l.closeLocations);
}
}
return ret;
}
}