Package org.apache.harmony.rmi.server

Source Code of org.apache.harmony.rmi.server.ClientDGC

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* @author  Mikhail A. Markov
* @version $Revision: 1.1.2.3 $
*/
package org.apache.harmony.rmi.server;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.rmi.RemoteException;
import java.rmi.dgc.DGC;
import java.rmi.dgc.Lease;
import java.rmi.dgc.VMID;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteRef;
import java.rmi.server.UID;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import org.apache.harmony.rmi.common.CreateThreadAction;
import org.apache.harmony.rmi.common.GetLongPropAction;
import org.apache.harmony.rmi.common.InterruptThreadAction;
import org.apache.harmony.rmi.common.RMIProperties;
import org.apache.harmony.rmi.internal.nls.Messages;
import org.apache.harmony.rmi.remoteref.UnicastRef;
import org.apache.harmony.rmi.transport.Endpoint;


/**
* DGC on Client's side - it's responsible for renewing/cancelling
* remote objects leases.
* It is made package protected for security reasons.
*
* @author  Mikhail A. Markov
* @version $Revision: 1.1.2.3 $
*/
class ClientDGC {

    /** ObjID for DGC. */
    public static final ObjID DGC_ID = new ObjID(ObjID.DGC_ID);

    /*
     * DGC ack timeout (in ms.) during which we hold strong refs to the objects.
     * Default value is 300000 ms (5 minutes).
     */
    private static final long dgcAckTimeout =
        ((Long) AccessController.doPrivileged(
                new GetLongPropAction(RMIProperties.DGCACKTIMEOUT_PROP,
                        5 * 60 * 1000))).longValue();
    /*
     * Max length of time (in ms.) between DGC.clean() calls in case of failed
     * calls. Default value is 180000 ms (3 minutes).
     */
    private static final long cleanInterval =
        ((Long) AccessController.doPrivileged(
                new GetLongPropAction(RMIProperties.DGCCLEANINTERVAL_PROP,
                        3 * 60 * 1000))).longValue();

    // VMID for this VM
    private static final VMID vmid = new VMID();

    /*
     * Sequentially increasing number for DGC calls.
     * It should be accessed vid getSeqNumber() method only.
     */
    private static long seqNum = Long.MIN_VALUE;

    // Table where Endpoint's are keys and RenewInfo are values.
    private static Hashtable epTable = new Hashtable();

    // List of strong refs to remoteObjects for referencing during DGC ack call.
    private static Hashtable dgcAckTable = new Hashtable();

    // Thread renewing leases.
    private static Thread lRenewer;

    // Thread detecting object which were garbage-collected.
    private static Thread roDetector = (Thread) AccessController.doPrivileged(
            new CreateThreadAction(new RemovedObjectsDetector(),
                    "RemovedObjectsDetector", true)); //$NON-NLS-1$

    // Timer handling events waiting for DGC ack messages.
    private static final Timer dgcAckTimer =
            (Timer) AccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
                    return new Timer(true);
                }});

    // DGC stub class
    private static Class dgcStubClass;

    // Queue for handling already collected objects.
    private static ReferenceQueue collectedQueue = new ReferenceQueue();

    static {

        // Initialize DGC implementation stub-class.
        try {
            dgcStubClass = Class.forName(
                "org.apache.harmony.rmi.server.DGCImpl_Stub"); //$NON-NLS-1$
        } catch (Exception ex) {
            // rmi.78=Unable to initialize ClientDGC.
            throw new Error(Messages.getString("rmi.78"), ex); //$NON-NLS-1$
        }

        // Start the thread detecting garbage-collected objects.
        roDetector.start();
    }

    /*
     * Registers the given RemoteRef in the table for sending DGC.dirty() calls
     * to the server, exported this object.
     */
    static void registerForRenew(RemoteRefBase ref) {
        RenewInfo info = (RenewInfo) epTable.get(ref.ep);

        if (info == null) {
            info = new RenewInfo(ref.ep);
            info.renewTime = System.currentTimeMillis();
            epTable.put(ref.ep, info);
            info.addToDirtySet(ref);
        } else if (info.addToDirtySet(ref)) {
            info.renewTime = System.currentTimeMillis();
        }

        if (lRenewer == null) {
            (lRenewer = (Thread) AccessController.doPrivileged(
                    new CreateThreadAction(new LeaseRenewer(),
                            "LeaseRenewer", true))).start(); //$NON-NLS-1$
        }
    }

    /*
     * Remove the given id for the specified Endpoint from the table and call
     * DGC.clean() method.
     */
    static void unregisterForRenew(Endpoint ep, ObjID id) {
        RenewInfo info = (RenewInfo) epTable.get(ep);

        if (info == null) {
            return;
        }

        // add ObjID to the list for DGC.clean() calls
        info.addToCleanSet(id);
    }

    /*
     * Adds the given object to the list of strong references associated with
     * the given UID.
     */
    static void registerForDGCAck(UID uid, Object obj) {
        RemoveTask task = (RemoveTask) dgcAckTable.get(uid);

        if (task == null) {
            task = new RemoveTask(uid);
            dgcAckTable.put(uid, task);
            dgcAckTimer.schedule(task, dgcAckTimeout);
        }
        task.addStrongRef(obj);
    }

    /*
     * Removes all strong refs associated with the given UID.
     */
    static void unregisterForDGCAck(UID uid) {
        TimerTask task = (TimerTask) dgcAckTable.get(uid);

        if (task == null) {
            return;
        }
        task.cancel();
        dgcAckTable.remove(uid);
    }

    /*
     * Returns next sequence number
     */
    private static synchronized long getSeqNumber() {
        return seqNum++;
    }


    /*
     * Auxiliary class for removing strong refs to the objects from the table.
     */
    private static class RemoveTask extends TimerTask {
        // UID for removing from the table
        private UID uid;

        // strong references to the objects
        private List strongRefs = new Vector();

        /*
         * Constructs RemoveTask for removing the given UID from the table.
         *
         * @param uid UID to be removed in the future by this task
         */
        RemoveTask(UID uid) {
            this.uid = uid;
        }

        /*
         * Removes strong refs to the objects associated with the stored UID
         * from the table;
         */
        public void run() {
            dgcAckTable.remove(uid);
        }

        /*
         * Adds strong ref to the given object to the list.
         *
         * @param obj Object strong reference to which should be stored
         */
        void addStrongRef(Object obj) {
            strongRefs.add(obj);
        }
    }


    /*
     * Phantom reference holding Endpoint and ObjID information for cancelling
     * leases after object is garbage-collected.
     */
    private static class PhantomRef extends PhantomReference {
        private Endpoint ep;
        private ObjID id;

        /*
         * Constructs PhantomRef. Calls super() with the given queue and obj and
         * stores ep and id.
         */
        PhantomRef(Object obj,
                   ReferenceQueue queue,
                   Endpoint ep,
                   ObjID id) {
            super(obj, queue);
            this.ep = ep;
            this.id = id;
        }
    }


    /*
     * Auxiliary class holding all info needed for leases renewing.
     */
    private static class RenewInfo {

        /*
         * Remote Endpoint where leases should be renewed.
         */
        private Endpoint ep;

        /*
         * Table where objIDs whose leases should be renewed are the keys
         * and PhantomRefs are the values.
         */
        private Hashtable renewTable = new Hashtable();

        /*
         * Table where objIDs whose leases should be cancelled are stored
         */
        private Set cleanSet = Collections.synchronizedSet(new HashSet());

        // Object for tables synchronization.
        private class TablesLock {}
        private Object tablesLock = new TablesLock();

        // When to renew leases.
        private long renewTime;

        // Initialized DGC stub.
        private DGC dgcStub;

        // Thread calling DGC.clean method
        private Thread cleanCaller = null;

        // Time when the first DGC.dirty call failed
        private long failureStartTime = 0;

        // Number of failed DGC.dirty calls.
        private int failedDirtyCallsNum = 0;

        // Base time for calculating renew time in case of failed dirty calls
        private long failedRenewBaseDuration = 0;

        // Lease duration returned by the latest successful DGC.dirty call.
        private long latestLeaseDuration = 0;

        /*
         * Initializes DGC stub and stores the given Endpoint.
         *
         * @param ep RemoteEndpoint where leases should be renewed
         */
        RenewInfo(Endpoint ep) {
            this.ep = ep;
            try {
                dgcStub = (DGC) dgcStubClass.getConstructor(
                        new Class[] { RemoteRef.class }).newInstance(
                                new Object[] { new UnicastRef(ep, DGC_ID) });
            } catch (Exception ex) {
                // rmi.79=Unable to initialized DGC stub.
                throw new Error(Messages.getString("rmi.79"), ex); //$NON-NLS-1$
            }
        }

        /*
         * Calls DGC.dirty() method.
         */
        void dgcDirty() {
            ObjID[] ids;

            synchronized (tablesLock) {
                ids = (ObjID[]) renewTable.keySet().toArray(
                        new ObjID[renewTable.size()]);
            }

            try {
                Lease lease = dgcStub.dirty(ids, getSeqNumber(),
                        new Lease(vmid, DGCImpl.maxDuration));
                failedDirtyCallsNum = 0;
                failureStartTime = 0;
                latestLeaseDuration = lease.getValue();
                renewTime = System.currentTimeMillis()
                        + latestLeaseDuration / 2;
            } catch (RemoteException re) {
                // dirty call failed
                long curTime = System.currentTimeMillis();
                ++failedDirtyCallsNum;

                if (failedDirtyCallsNum == 1) {
                    failureStartTime = curTime;
                    latestLeaseDuration = (latestLeaseDuration > 0)
                            ? latestLeaseDuration : DGCImpl.maxDuration;
                    failedRenewBaseDuration = latestLeaseDuration >> 5;
                }
                renewTime = curTime +
                    failedRenewBaseDuration * ((failedDirtyCallsNum - 1) << 1);

                if (renewTime > failureStartTime + latestLeaseDuration) {
                    renewTime = Long.MAX_VALUE;
                }
            }
        }

        /*
         * Adds ObjID of the given ref to the set of objIDs whose leases should
         * be renewed, creates a PhantomReference for the object for sending
         * clean request to the server's DGC when the object is
         * garbage-collected.
         *
         * @param ref UnicastRef to be registered
         *
         * @return true if this RenewInfo already contained the given ref and
         *         false otherwise
         */
        boolean addToDirtySet(RemoteRefBase ref) {
            synchronized (tablesLock) {
                ObjID id = ref.getObjId();

                if (renewTable.containsKey(id)) {
                    return true;
                }
                renewTable.put(id, new PhantomRef(
                        ref, collectedQueue, ep, id));
                return false;
            }
        }

        /*
         * Adds the given ObjID to the list of objects for DGC.clean() method
         * call and removes it from the dirty set.
         *
         * @param id ObjID of remote object
         */
        void addToCleanSet(ObjID id) {
            synchronized (tablesLock) {
                renewTable.remove(id);
                cleanSet.add(id);

                if (cleanCaller == null) {
                    (cleanCaller = ((Thread) AccessController.doPrivileged(
                            new CreateThreadAction(new CleanCaller(this),
                                    "CleanCaller for " + ep, true)))).start(); //$NON-NLS-1$
                } else {
                    AccessController.doPrivileged(
                            new InterruptThreadAction(cleanCaller));
                }
            }
        }
    }


    /*
     * Auxiliary class renewing leases.
     */
    private static class LeaseRenewer implements Runnable {
        /**
         * Iterates over epTable and renews leases.
         */
        public void run() {
            do {
                long curTime = System.currentTimeMillis();
                long awakeTime = curTime + DGCImpl.maxDuration / 2;

                try {
                    synchronized (epTable) {
                        for (Enumeration eps = epTable.keys();
                                eps.hasMoreElements();) {
                            Endpoint ep = (Endpoint) eps.nextElement();
                            RenewInfo info = (RenewInfo) epTable.get(ep);

                            if (info.renewTime <= curTime) {
                                // we should renew lease for this ids
                                info.dgcDirty();
                            }

                            if (info.renewTime < awakeTime) {
                                awakeTime = info.renewTime;
                            }
                        }
                    }

                    if (awakeTime > curTime) {
                        Thread.sleep(awakeTime - curTime);
                    }
                } catch (InterruptedException ie) {
                }
            } while (epTable.size() != 0);
            lRenewer = null;
        }
    }


    /*
     * Auxiliary thread detecting remote objects which where garbage-collected
     * and spawning the thread calling DGC.clean() method.
     */
    private static class RemovedObjectsDetector implements Runnable {

        /**
         * Thread sitting on blocking ReferenceQueue.remove() method and when
         * it returns PhantomRef - removing the object from the dirty set and
         * calling DGC.clean() method.
         */
        public void run() {
            do {
                try {
                    // this operation blocks the thread
                    PhantomRef ref = (PhantomRef) collectedQueue.remove();

                    unregisterForRenew(ref.ep, ref.id);
                } catch (InterruptedException ie) {
                }
            } while (true);
        }
    }


    /*
     * Auxiliary class calling DGC.clean() method on server side.
     */
    private static class CleanCaller implements Runnable {
        // RenewInfo where this class was created
        private RenewInfo info;

        // Number of failed DGC.clean calls.
        private int failedCleanCallsNum = 0;

        /*
         * Constructs CleanCaller holding the reference to the given RenewInfo.
         */
        CleanCaller(RenewInfo info) {
            this.info = info;
        }

        /**
         * Call DGC.clean() method, in case of failed call retry it in a loop.
         */
        public void run() {
            while (true) {
                boolean success = true;
                Set curCleanSet;

                synchronized (info.tablesLock) {
                    if (info.cleanSet.isEmpty()) {
                        break;
                    }
                    curCleanSet = new HashSet(info.cleanSet);
                }

                try {
                    info.dgcStub.clean((ObjID[]) curCleanSet.toArray(
                            new ObjID[curCleanSet.size()]), getSeqNumber(),
                            vmid, info.failedDirtyCallsNum == 0);

                    // DGC.clean() call succeeded
                    synchronized (info.tablesLock) {
                        info.cleanSet.remove(curCleanSet);

                        if (info.cleanSet.isEmpty()) {
                            break;
                        }
                    }
                } catch(RemoteException re) {
                    // DGC.clean() call failed
                    success = false;
                    failedCleanCallsNum++;

                    if (failedCleanCallsNum > 4) {
                        synchronized (info.tablesLock) {
                            if (!info.renewTable.isEmpty()) {
                                info.cleanSet.clear();
                            }
                        }
                        break;
                    }
                }

                if (Thread.interrupted()) {
                    continue;
                }

                /*
                 * If DGC.clean() call failed we should wait for a cleanInterval
                 * period of time.
                 */
                if (!success) {
                    try {
                        Thread.sleep(cleanInterval);
                    } catch(InterruptedException ie) {
                        continue;
                    }
                }
            }

            synchronized (info.tablesLock) {
                if (!info.renewTable.isEmpty()) {
                    epTable.remove(info.ep);
                }
            }
            info.cleanCaller = null;
        }
    }
}
TOP

Related Classes of org.apache.harmony.rmi.server.ClientDGC

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.