All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hudson.remoting.ExportTable Maven / Gradle / Ivy

Go to download

Contains the bootstrap code to bridge separate JVMs into a single semi-shared space. Reusable outside Hudson.

There is a newer version: 3.0.3
Show newest version
/*******************************************************************************
 *
 * 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); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy