| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
package sun.rmi.transport;  | 
 | 
 | 
 | 
import java.net.SocketPermission;  | 
 | 
import java.rmi.Remote;  | 
 | 
import java.rmi.RemoteException;  | 
 | 
import java.rmi.dgc.DGC;  | 
 | 
import java.rmi.dgc.Lease;  | 
 | 
import java.rmi.dgc.VMID;  | 
 | 
import java.rmi.server.LogStream;  | 
 | 
import java.rmi.server.ObjID;  | 
 | 
import java.rmi.server.RemoteServer;  | 
 | 
import java.rmi.server.ServerNotActiveException;  | 
 | 
import java.rmi.server.UID;  | 
 | 
import java.security.AccessControlContext;  | 
 | 
import java.security.AccessController;  | 
 | 
import java.security.Permissions;  | 
 | 
import java.security.PrivilegedAction;  | 
 | 
import java.security.ProtectionDomain;  | 
 | 
import java.security.Security;  | 
 | 
import java.util.ArrayList;  | 
 | 
import java.util.HashSet;  | 
 | 
import java.util.HashMap;  | 
 | 
import java.util.Iterator;  | 
 | 
import java.util.List;  | 
 | 
import java.util.Map;  | 
 | 
import java.util.Set;  | 
 | 
import java.util.concurrent.Future;  | 
 | 
import java.util.concurrent.ScheduledExecutorService;  | 
 | 
import java.util.concurrent.TimeUnit;  | 
 | 
 | 
 | 
import sun.misc.ObjectInputFilter;  | 
 | 
 | 
 | 
import sun.rmi.runtime.Log;  | 
 | 
import sun.rmi.runtime.RuntimeUtil;  | 
 | 
import sun.rmi.server.UnicastRef;  | 
 | 
import sun.rmi.server.UnicastServerRef;  | 
 | 
import sun.rmi.server.Util;  | 
 | 
import sun.security.action.GetLongAction;  | 
 | 
import sun.security.action.GetPropertyAction;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
@SuppressWarnings("deprecation") | 
 | 
final class DGCImpl implements DGC { | 
 | 
 | 
 | 
      | 
 | 
    static final Log dgcLog = Log.getLog("sun.rmi.dgc", "dgc", | 
 | 
        LogStream.parseLevel(AccessController.doPrivileged(  | 
 | 
            new GetPropertyAction("sun.rmi.dgc.logLevel")))); | 
 | 
 | 
 | 
      | 
 | 
    private static final long leaseValue =                | 
 | 
        AccessController.doPrivileged(  | 
 | 
            new GetLongAction("java.rmi.dgc.leaseValue", 600000)); | 
 | 
 | 
 | 
      | 
 | 
    private static final long leaseCheckInterval =  | 
 | 
        AccessController.doPrivileged(  | 
 | 
            new GetLongAction("sun.rmi.dgc.checkInterval", leaseValue / 2)); | 
 | 
 | 
 | 
      | 
 | 
    private static final ScheduledExecutorService scheduler =  | 
 | 
        AccessController.doPrivileged(  | 
 | 
            new RuntimeUtil.GetInstanceAction()).getScheduler();  | 
 | 
 | 
 | 
      | 
 | 
    private static DGCImpl dgc;  | 
 | 
      | 
 | 
    private Map<VMID,LeaseInfo> leaseTable = new HashMap<>();  | 
 | 
      | 
 | 
    private Future<?> checker = null;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    static DGCImpl getDGCImpl() { | 
 | 
        return dgc;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static final String DGC_FILTER_PROPNAME = "sun.rmi.transport.dgcFilter";  | 
 | 
 | 
 | 
      | 
 | 
    private static int DGC_MAX_DEPTH = 5;  | 
 | 
 | 
 | 
      | 
 | 
    private static int DGC_MAX_ARRAY_SIZE = 10000;  | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static final ObjectInputFilter dgcFilter =  | 
 | 
            AccessController.doPrivileged((PrivilegedAction<ObjectInputFilter>)DGCImpl::initDgcFilter);  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static ObjectInputFilter initDgcFilter() { | 
 | 
        ObjectInputFilter filter = null;  | 
 | 
        String props = System.getProperty(DGC_FILTER_PROPNAME);  | 
 | 
        if (props == null) { | 
 | 
            props = Security.getProperty(DGC_FILTER_PROPNAME);  | 
 | 
        }  | 
 | 
        if (props != null) { | 
 | 
            filter = ObjectInputFilter.Config.createFilter(props);  | 
 | 
            if (dgcLog.isLoggable(Log.BRIEF)) { | 
 | 
                dgcLog.log(Log.BRIEF, "dgcFilter = " + filter);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return filter;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private DGCImpl() {} | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public Lease dirty(ObjID[] ids, long sequenceNum, Lease lease) { | 
 | 
        VMID vmid = lease.getVMID();  | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        long duration = leaseValue;  | 
 | 
 | 
 | 
        if (dgcLog.isLoggable(Log.VERBOSE)) { | 
 | 
            dgcLog.log(Log.VERBOSE, "vmid = " + vmid);  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        if (vmid == null) { | 
 | 
            vmid = new VMID();  | 
 | 
 | 
 | 
            if (dgcLog.isLoggable(Log.BRIEF)) { | 
 | 
                String clientHost;  | 
 | 
                try { | 
 | 
                    clientHost = RemoteServer.getClientHost();  | 
 | 
                } catch (ServerNotActiveException e) { | 
 | 
                    clientHost = "<unknown host>";  | 
 | 
                }  | 
 | 
                dgcLog.log(Log.BRIEF, " assigning vmid " + vmid +  | 
 | 
                           " to client " + clientHost);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        lease = new Lease(vmid, duration);  | 
 | 
          | 
 | 
        synchronized (leaseTable) { | 
 | 
            LeaseInfo info = leaseTable.get(vmid);  | 
 | 
            if (info == null) { | 
 | 
                leaseTable.put(vmid, new LeaseInfo(vmid, duration));  | 
 | 
                if (checker == null) { | 
 | 
                    checker = scheduler.scheduleWithFixedDelay(  | 
 | 
                        new Runnable() { | 
 | 
                            public void run() { | 
 | 
                                checkLeases();  | 
 | 
                            }  | 
 | 
                        },  | 
 | 
                        leaseCheckInterval,  | 
 | 
                        leaseCheckInterval, TimeUnit.MILLISECONDS);  | 
 | 
                }  | 
 | 
            } else { | 
 | 
                info.renew(duration);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        for (ObjID id : ids) { | 
 | 
            if (dgcLog.isLoggable(Log.VERBOSE)) { | 
 | 
                dgcLog.log(Log.VERBOSE, "id = " + id +  | 
 | 
                           ", vmid = " + vmid + ", duration = " + duration);  | 
 | 
            }  | 
 | 
 | 
 | 
            ObjectTable.referenced(id, sequenceNum, vmid);  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        return lease;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public void clean(ObjID[] ids, long sequenceNum, VMID vmid, boolean strong)  | 
 | 
    { | 
 | 
        for (ObjID id : ids) { | 
 | 
            if (dgcLog.isLoggable(Log.VERBOSE)) { | 
 | 
                dgcLog.log(Log.VERBOSE, "id = " + id +  | 
 | 
                    ", vmid = " + vmid + ", strong = " + strong);  | 
 | 
            }  | 
 | 
 | 
 | 
            ObjectTable.unreferenced(id, sequenceNum, vmid, strong);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    void registerTarget(VMID vmid, Target target) { | 
 | 
        synchronized (leaseTable) { | 
 | 
            LeaseInfo info = leaseTable.get(vmid);  | 
 | 
            if (info == null) { | 
 | 
                target.vmidDead(vmid);  | 
 | 
            } else { | 
 | 
                info.notifySet.add(target);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    void unregisterTarget(VMID vmid, Target target) { | 
 | 
        synchronized (leaseTable) { | 
 | 
            LeaseInfo info = leaseTable.get(vmid);  | 
 | 
            if (info != null) { | 
 | 
                info.notifySet.remove(target);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private void checkLeases() { | 
 | 
        long time = System.currentTimeMillis();  | 
 | 
 | 
 | 
          | 
 | 
        List<LeaseInfo> toUnregister = new ArrayList<>();  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        synchronized (leaseTable) { | 
 | 
            Iterator<LeaseInfo> iter = leaseTable.values().iterator();  | 
 | 
            while (iter.hasNext()) { | 
 | 
                LeaseInfo info = iter.next();  | 
 | 
                if (info.expired(time)) { | 
 | 
                    toUnregister.add(info);  | 
 | 
                    iter.remove();  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            if (leaseTable.isEmpty()) { | 
 | 
                checker.cancel(false);  | 
 | 
                checker = null;  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
         */  | 
 | 
        for (LeaseInfo info : toUnregister) { | 
 | 
            for (Target target : info.notifySet) { | 
 | 
                target.vmidDead(info.vmid);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    static { | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        AccessController.doPrivileged(new PrivilegedAction<Void>() { | 
 | 
            public Void run() { | 
 | 
                ClassLoader savedCcl =  | 
 | 
                    Thread.currentThread().getContextClassLoader();  | 
 | 
                try { | 
 | 
                    Thread.currentThread().setContextClassLoader(  | 
 | 
                        ClassLoader.getSystemClassLoader());  | 
 | 
 | 
 | 
                      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
                     */  | 
 | 
                    try { | 
 | 
                        dgc = new DGCImpl();  | 
 | 
                        ObjID dgcID = new ObjID(ObjID.DGC_ID);  | 
 | 
                        LiveRef ref = new LiveRef(dgcID, 0);  | 
 | 
                        UnicastServerRef disp = new UnicastServerRef(ref,  | 
 | 
                                DGCImpl::checkInput);  | 
 | 
                        Remote stub =  | 
 | 
                            Util.createProxy(DGCImpl.class,  | 
 | 
                                             new UnicastRef(ref), true);  | 
 | 
                        disp.setSkeleton(dgc);  | 
 | 
 | 
 | 
                        Permissions perms = new Permissions();  | 
 | 
                        perms.add(new SocketPermission("*", "accept,resolve")); | 
 | 
                        ProtectionDomain[] pd = { new ProtectionDomain(null, perms) }; | 
 | 
                        AccessControlContext acceptAcc = new AccessControlContext(pd);  | 
 | 
 | 
 | 
                        Target target = AccessController.doPrivileged(  | 
 | 
                            new PrivilegedAction<Target>() { | 
 | 
                                public Target run() { | 
 | 
                                    return new Target(dgc, disp, stub, dgcID, true);  | 
 | 
                                }  | 
 | 
                            }, acceptAcc);  | 
 | 
 | 
 | 
                        ObjectTable.putTarget(target);  | 
 | 
                    } catch (RemoteException e) { | 
 | 
                        throw new Error(  | 
 | 
                            "exception initializing server-side DGC", e);  | 
 | 
                    }  | 
 | 
                } finally { | 
 | 
                    Thread.currentThread().setContextClassLoader(savedCcl);  | 
 | 
                }  | 
 | 
                return null;  | 
 | 
            }  | 
 | 
        });  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static ObjectInputFilter.Status checkInput(ObjectInputFilter.FilterInfo filterInfo) { | 
 | 
        if (dgcFilter != null) { | 
 | 
            ObjectInputFilter.Status status = dgcFilter.checkInput(filterInfo);  | 
 | 
            if (status != ObjectInputFilter.Status.UNDECIDED) { | 
 | 
                  | 
 | 
                return status;  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        if (filterInfo.depth() > DGC_MAX_DEPTH) { | 
 | 
            return ObjectInputFilter.Status.REJECTED;  | 
 | 
        }  | 
 | 
        Class<?> clazz = filterInfo.serialClass();  | 
 | 
        if (clazz != null) { | 
 | 
            while (clazz.isArray()) { | 
 | 
                if (filterInfo.arrayLength() >= 0 && filterInfo.arrayLength() > DGC_MAX_ARRAY_SIZE) { | 
 | 
                    return ObjectInputFilter.Status.REJECTED;  | 
 | 
                }  | 
 | 
                  | 
 | 
                clazz = clazz.getComponentType();  | 
 | 
            }  | 
 | 
            if (clazz.isPrimitive()) { | 
 | 
                  | 
 | 
                return ObjectInputFilter.Status.ALLOWED;  | 
 | 
            }  | 
 | 
            return (clazz == ObjID.class ||  | 
 | 
                    clazz == UID.class ||  | 
 | 
                    clazz == VMID.class ||  | 
 | 
                    clazz == Lease.class)  | 
 | 
                    ? ObjectInputFilter.Status.ALLOWED  | 
 | 
                    : ObjectInputFilter.Status.REJECTED;  | 
 | 
        }  | 
 | 
          | 
 | 
        return ObjectInputFilter.Status.UNDECIDED;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    private static class LeaseInfo { | 
 | 
        VMID vmid;  | 
 | 
        long expiration;  | 
 | 
        Set<Target> notifySet = new HashSet<>();  | 
 | 
 | 
 | 
        LeaseInfo(VMID vmid, long lease) { | 
 | 
            this.vmid = vmid;  | 
 | 
            expiration = System.currentTimeMillis() + lease;  | 
 | 
        }  | 
 | 
 | 
 | 
        synchronized void renew(long lease) { | 
 | 
            long newExpiration = System.currentTimeMillis() + lease;  | 
 | 
            if (newExpiration > expiration)  | 
 | 
                expiration = newExpiration;  | 
 | 
        }  | 
 | 
 | 
 | 
        boolean expired(long time) { | 
 | 
            if (expiration < time) { | 
 | 
                if (dgcLog.isLoggable(Log.BRIEF)) { | 
 | 
                    dgcLog.log(Log.BRIEF, vmid.toString());  | 
 | 
                }  | 
 | 
                return true;  | 
 | 
            } else { | 
 | 
                return false;  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |