hudson.remoting.ExportTable Maven / Gradle / Ivy
Show all versions of hudson-remoting Show documentation
/*******************************************************************************
*
* Copyright (c) 2004-2009 Oracle Corporation.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*
* Kohsuke Kawaguchi
*
*
*******************************************************************************/
package hudson.remoting;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;
/**
* Manages unique ID for exported objects, and allows look-up from IDs.
*
* @author Kohsuke Kawaguchi
*/
final class ExportTable {
private final Map table = new HashMap();
private final Map reverse = new HashMap();
/**
* {@link ExportList}s which are actively recording the current
* export operation.
*/
private final ThreadLocal lists = new ThreadLocal();
/**
* Information about one exporetd object.
*/
private final class Entry {
final int id;
final T object;
/**
* Where was this object first exported?
*/
final Exception allocationTrace;
/**
* Current reference count.
* Access to {@link ExportTable} is guarded by synchronized block,
* so accessing this field requires no further synchronization.
*/
private int referenceCount;
Entry(T object) {
this.id = iota++;
this.object = object;
this.allocationTrace = new Exception();
// force the computation of the stack trace in a Java friendly data structure,
// so that the call stack can be seen from the heap dump after the fact.
allocationTrace.getStackTrace();
addRef();
table.put(id,this);
reverse.put(object,this);
}
void addRef() {
referenceCount++;
}
void release() {
if(--referenceCount==0) {
table.remove(id);
reverse.remove(object);
}
}
}
/**
* Captures the list of export, so that they can be unexported later.
*
* This is tied to a particular thread, so it only records operations
* on the current thread.
*/
public final class ExportList extends ArrayList {
private final ExportList old;
private ExportList() {
old=lists.get();
lists.set(this);
}
void release() {
synchronized(ExportTable.this) {
for (Entry e : this)
e.release();
}
}
void stopRecording() {
lists.set(old);
}
}
/**
* Unique ID generator.
*/
private int iota = 1;
/**
* Starts the recording of the export operations
* and returns the list that captures the result.
*
* @see ExportList#stopRecording()
*/
public ExportList startRecording() {
ExportList el = new ExportList();
lists.set(el);
return el;
}
public boolean isRecording() {
return lists.get()!=null;
}
/**
* Exports the given object.
*
*
* Until the object is {@link #unexport(Object) unexported}, it will
* not be subject to GC.
*
* @return
* The assigned 'object ID'. If the object is already exported,
* it will return the ID already assigned to it.
*/
public synchronized int export(T t) {
return export(t,true);
}
/**
* @param notifyListener
* If false, listener will not be notified. This is used to
* create an export that won't get unexported when the call returns.
*/
public synchronized int export(T t, boolean notifyListener) {
if(t==null) return 0; // bootstrap classloader
Entry e = reverse.get(t);
if(e==null)
e = new Entry(t);
else
e.addRef();
if(notifyListener) {
ExportList l = lists.get();
if(l!=null) l.add(e);
}
return e.id;
}
public synchronized T get(int id) {
Entry e = table.get(id);
if(e!=null) return e.object;
else return null;
}
/**
* Removes the exported object from the table.
*/
public synchronized void unexport(T t) {
if(t==null) return;
Entry e = reverse.get(t);
if(e==null) return; // presumably already unexported
e.release();
}
/**
* Removes the exported object for the specified oid from the table.
*/
public synchronized void unexportByOid(Integer oid) {
if(oid==null) return;
Entry e = table.get(oid);
if(e==null) return; // presumably already unexported
e.release();
}
/**
* Dumps the contents of the table to a file.
*/
public synchronized void dump(PrintWriter w) throws IOException {
for (Entry e : table.values()) {
w.printf("#%d (ref.%d) : %s\n", e.id, e.referenceCount, e.object);
e.allocationTrace.printStackTrace(w);
}
}
}