alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (ObjectTable.java)

This example Java source code file (ObjectTable.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

error, exportexception, hashmap, map, nosuchobjectexception, object, objectendpoint, objecttable, reaper, referencequeue, remote, rmi, security, target, util, vmid, weakref

The ObjectTable.java Java example source code

/*
 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package sun.rmi.transport;

import java.lang.ref.ReferenceQueue;
import java.rmi.NoSuchObjectException;
import java.rmi.Remote;
import java.rmi.dgc.VMID;
import java.rmi.server.ExportException;
import java.rmi.server.ObjID;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.Map;
import sun.misc.GC;
import sun.rmi.runtime.Log;
import sun.rmi.runtime.NewThreadAction;
import sun.security.action.GetLongAction;

/**
 * Object table shared by all implementors of the Transport interface.
 * This table maps object ids to remote object targets in this address
 * space.
 *
 * @author  Ann Wollrath
 * @author  Peter Jones
 */
public final class ObjectTable {

    /** maximum interval between complete garbage collections of local heap */
    private final static long gcInterval =              // default 1 hour
        AccessController.doPrivileged(
            new GetLongAction("sun.rmi.dgc.server.gcInterval", 3600000));

    /**
     * lock guarding objTable and implTable.
     * Holders MAY acquire a Target instance's lock or keepAliveLock.
     */
    private static final Object tableLock = new Object();

    /** tables mapping to Target, keyed from ObjectEndpoint and impl object */
    private static final Map<ObjectEndpoint,Target> objTable =
        new HashMap<>();
    private static final Map<WeakRef,Target> implTable =
        new HashMap<>();

    /**
     * lock guarding keepAliveCount, reaper, and gcLatencyRequest.
     * Holders may NOT acquire a Target instance's lock or tableLock.
     */
    private static final Object keepAliveLock = new Object();

    /** count of non-permanent objects in table or still processing calls */
    private static int keepAliveCount = 0;

    /** thread to collect unreferenced objects from table */
    private static Thread reaper = null;

    /** queue notified when weak refs in the table are cleared */
    static final ReferenceQueue<Object> reapQueue = new ReferenceQueue<>();

    /** handle for GC latency request (for future cancellation) */
    private static GC.LatencyRequest gcLatencyRequest = null;

    /*
     * Disallow anyone from creating one of these.
     */
    private ObjectTable() {}

    /**
     * Returns the target associated with the object id.
     */
    static Target getTarget(ObjectEndpoint oe) {
        synchronized (tableLock) {
            return objTable.get(oe);
        }
    }

    /**
     * Returns the target associated with the remote object
     */
    public static Target getTarget(Remote impl) {
        synchronized (tableLock) {
            return implTable.get(new WeakRef(impl));
        }
    }

    /**
     * Returns the stub for the remote object <b>obj passed
     * as a parameter. This operation is only valid <i>after
     * the object has been exported.
     *
     * @return the stub for the remote object, <b>obj.
     * @exception NoSuchObjectException if the stub for the
     * remote object could not be found.
     */
    public static Remote getStub(Remote impl)
        throws NoSuchObjectException
    {
        Target target = getTarget(impl);
        if (target == null) {
            throw new NoSuchObjectException("object not exported");
        } else {
            return target.getStub();
        }
    }

   /**
    * Remove the remote object, obj, from the RMI runtime. If
    * successful, the object can no longer accept incoming RMI calls.
    * If the force parameter is true, the object is forcibly unexported
    * even if there are pending calls to the remote object or the
    * remote object still has calls in progress.  If the force
    * parameter is false, the object is only unexported if there are
    * no pending or in progress calls to the object.
    *
    * @param obj the remote object to be unexported
    * @param force if true, unexports the object even if there are
    * pending or in-progress calls; if false, only unexports the object
    * if there are no pending or in-progress calls
    * @return true if operation is successful, false otherwise
    * @exception NoSuchObjectException if the remote object is not
    * currently exported
    */
   public static boolean unexportObject(Remote obj, boolean force)
        throws java.rmi.NoSuchObjectException
    {
        synchronized (tableLock) {
            Target target = getTarget(obj);
            if (target == null) {
                throw new NoSuchObjectException("object not exported");
            } else {
                if (target.unexport(force)) {
                    removeTarget(target);
                    return true;
                } else {
                    return false;
                }
            }
        }
    }

    /**
     * Add target to object table.  If it is not a permanent entry, then
     * make sure that reaper thread is running to remove collected entries
     * and keep VM alive.
     */
    static void putTarget(Target target) throws ExportException {
        ObjectEndpoint oe = target.getObjectEndpoint();
        WeakRef weakImpl = target.getWeakImpl();

        if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
            DGCImpl.dgcLog.log(Log.VERBOSE, "add object " + oe);
        }

        synchronized (tableLock) {
            /**
             * Do nothing if impl has already been collected (see 6597112). Check while
             * holding tableLock to ensure that Reaper cannot process weakImpl in between
             * null check and put/increment effects.
             */
            if (target.getImpl() != null) {
                if (objTable.containsKey(oe)) {
                    throw new ExportException(
                        "internal error: ObjID already in use");
                } else if (implTable.containsKey(weakImpl)) {
                    throw new ExportException("object already exported");
                }

                objTable.put(oe, target);
                implTable.put(weakImpl, target);

                if (!target.isPermanent()) {
                    incrementKeepAliveCount();
                }
            }
        }
    }

    /**
     * Remove target from object table.
     *
     * NOTE: This method must only be invoked while synchronized on
     * the "tableLock" object, because it does not do so itself.
     */
    private static void removeTarget(Target target) {
        // assert Thread.holdsLock(tableLock);

        ObjectEndpoint oe = target.getObjectEndpoint();
        WeakRef weakImpl = target.getWeakImpl();

        if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
            DGCImpl.dgcLog.log(Log.VERBOSE, "remove object " + oe);
        }

        objTable.remove(oe);
        implTable.remove(weakImpl);

        target.markRemoved();   // handles decrementing keep-alive count
    }

    /**
     * Process client VM signalling reference for given ObjID: forward to
     * corresponding Target entry.  If ObjID is not found in table,
     * no action is taken.
     */
    static void referenced(ObjID id, long sequenceNum, VMID vmid) {
        synchronized (tableLock) {
            ObjectEndpoint oe =
                new ObjectEndpoint(id, Transport.currentTransport());
            Target target = objTable.get(oe);
            if (target != null) {
                target.referenced(sequenceNum, vmid);
            }
        }
    }

    /**
     * Process client VM dropping reference for given ObjID: forward to
     * corresponding Target entry.  If ObjID is not found in table,
     * no action is taken.
     */
    static void unreferenced(ObjID id, long sequenceNum, VMID vmid,
                             boolean strong)
    {
        synchronized (tableLock) {
            ObjectEndpoint oe =
                new ObjectEndpoint(id, Transport.currentTransport());
            Target target = objTable.get(oe);
            if (target != null)
                target.unreferenced(sequenceNum, vmid, strong);
        }
    }

    /**
     * Increments the "keep-alive count".
     *
     * The "keep-alive count" is the number of non-permanent remote objects
     * that are either in the object table or still have calls in progress.
     * Therefore, this method should be invoked exactly once for every
     * non-permanent remote object exported (a remote object must be
     * exported before it can have any calls in progress).
     *
     * The VM is "kept alive" while the keep-alive count is greater than
     * zero; this is accomplished by keeping a non-daemon thread running.
     *
     * Because non-permanent objects are those that can be garbage
     * collected while exported, and thus those for which the "reaper"
     * thread operates, the reaper thread also serves as the non-daemon
     * VM keep-alive thread; a new reaper thread is created if necessary.
     */
    static void incrementKeepAliveCount() {
        synchronized (keepAliveLock) {
            keepAliveCount++;

            if (reaper == null) {
                reaper = AccessController.doPrivileged(
                    new NewThreadAction(new Reaper(), "Reaper", false));
                reaper.start();
            }

            /*
             * While there are non-"permanent" objects in the object table,
             * request a maximum latency for inspecting the entire heap
             * from the local garbage collector, to place an upper bound
             * on the time to discover remote objects that have become
             * unreachable (and thus can be removed from the table).
             */
            if (gcLatencyRequest == null) {
                gcLatencyRequest = GC.requestLatency(gcInterval);
            }
        }
    }

    /**
     * Decrements the "keep-alive count".
     *
     * The "keep-alive count" is the number of non-permanent remote objects
     * that are either in the object table or still have calls in progress.
     * Therefore, this method should be invoked exactly once for every
     * previously-exported non-permanent remote object that both has been
     * removed from the object table and has no calls still in progress.
     *
     * If the keep-alive count is decremented to zero, then the current
     * reaper thread is terminated to cease keeping the VM alive (and
     * because there are no more non-permanent remote objects to reap).
     */
    static void decrementKeepAliveCount() {
        synchronized (keepAliveLock) {
            keepAliveCount--;

            if (keepAliveCount == 0) {
                if (!(reaper != null)) { throw new AssertionError(); }
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        reaper.interrupt();
                        return null;
                    }
                });
                reaper = null;

                /*
                 * If there are no longer any non-permanent objects in the
                 * object table, we are no longer concerned with the latency
                 * of local garbage collection here.
                 */
                gcLatencyRequest.cancel();
                gcLatencyRequest = null;
            }
        }
    }

    /**
     * The Reaper thread waits for notifications that weak references in the
     * object table have been cleared.  When it receives a notification, it
     * removes the corresponding entry from the table.
     *
     * Since the Reaper is created as a non-daemon thread, it also serves
     * to keep the VM from exiting while there are objects in the table
     * (other than permanent entries that should neither be reaped nor
     * keep the VM alive).
     */
    private static class Reaper implements Runnable {

        public void run() {
            try {
                do {
                    // wait for next cleared weak reference
                    WeakRef weakImpl = (WeakRef) reapQueue.remove();

                    synchronized (tableLock) {
                        Target target = implTable.get(weakImpl);
                        if (target != null) {
                            if (!target.isEmpty()) {
                                throw new Error(
                                    "object with known references collected");
                            } else if (target.isPermanent()) {
                                throw new Error("permanent object collected");
                            }
                            removeTarget(target);
                        }
                    }
                } while (!Thread.interrupted());
            } catch (InterruptedException e) {
                // pass away if interrupted
            }
        }
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java ObjectTable.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.