Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/rmi/transport/DGCImpl.java
38831 views
/*1* Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package sun.rmi.transport;2526import java.net.SocketPermission;27import java.rmi.Remote;28import java.rmi.RemoteException;29import java.rmi.dgc.DGC;30import java.rmi.dgc.Lease;31import java.rmi.dgc.VMID;32import java.rmi.server.LogStream;33import java.rmi.server.ObjID;34import java.rmi.server.RemoteServer;35import java.rmi.server.ServerNotActiveException;36import java.rmi.server.UID;37import java.security.AccessControlContext;38import java.security.AccessController;39import java.security.Permissions;40import java.security.PrivilegedAction;41import java.security.ProtectionDomain;42import java.security.Security;43import java.util.ArrayList;44import java.util.HashSet;45import java.util.HashMap;46import java.util.Iterator;47import java.util.List;48import java.util.Map;49import java.util.Set;50import java.util.concurrent.Future;51import java.util.concurrent.ScheduledExecutorService;52import java.util.concurrent.TimeUnit;5354import sun.misc.ObjectInputFilter;5556import sun.rmi.runtime.Log;57import sun.rmi.runtime.RuntimeUtil;58import sun.rmi.server.UnicastRef;59import sun.rmi.server.UnicastServerRef;60import sun.rmi.server.Util;61import sun.security.action.GetLongAction;62import sun.security.action.GetPropertyAction;6364/**65* This class implements the guts of the server-side distributed GC66* algorithm67*68* @author Ann Wollrath69*/70@SuppressWarnings("deprecation")71final class DGCImpl implements DGC {7273/* dgc system log */74static final Log dgcLog = Log.getLog("sun.rmi.dgc", "dgc",75LogStream.parseLevel(AccessController.doPrivileged(76new GetPropertyAction("sun.rmi.dgc.logLevel"))));7778/** lease duration to grant to clients */79private static final long leaseValue = // default 10 minutes80AccessController.doPrivileged(81new GetLongAction("java.rmi.dgc.leaseValue", 600000));8283/** lease check interval; default is half of lease grant duration */84private static final long leaseCheckInterval =85AccessController.doPrivileged(86new GetLongAction("sun.rmi.dgc.checkInterval", leaseValue / 2));8788/** thread pool for scheduling delayed tasks */89private static final ScheduledExecutorService scheduler =90AccessController.doPrivileged(91new RuntimeUtil.GetInstanceAction()).getScheduler();9293/** remote implementation of DGC interface for this VM */94private static DGCImpl dgc;95/** table that maps VMID to LeaseInfo */96private Map<VMID,LeaseInfo> leaseTable = new HashMap<>();97/** checks for lease expiration */98private Future<?> checker = null;99100/**101* Return the remote implementation of the DGC interface for102* this VM.103*/104static DGCImpl getDGCImpl() {105return dgc;106}107108/**109* Property name of the DGC serial filter to augment110* the built-in list of allowed types.111* Setting the property in the {@code lib/security/java.security} file112* will enable the augmented filter.113*/114private static final String DGC_FILTER_PROPNAME = "sun.rmi.transport.dgcFilter";115116/** Registry max depth of remote invocations. **/117private static int DGC_MAX_DEPTH = 5;118119/** Registry maximum array size in remote invocations. **/120private static int DGC_MAX_ARRAY_SIZE = 10000;121/**122* The dgcFilter created from the value of the {@code "sun.rmi.transport.dgcFilter"}123* property.124*/125private static final ObjectInputFilter dgcFilter =126AccessController.doPrivileged((PrivilegedAction<ObjectInputFilter>)DGCImpl::initDgcFilter);127128/**129* Initialize the dgcFilter from the security properties or system property; if any130* @return an ObjectInputFilter, or null131*/132private static ObjectInputFilter initDgcFilter() {133ObjectInputFilter filter = null;134String props = System.getProperty(DGC_FILTER_PROPNAME);135if (props == null) {136props = Security.getProperty(DGC_FILTER_PROPNAME);137}138if (props != null) {139filter = ObjectInputFilter.Config.createFilter(props);140if (dgcLog.isLoggable(Log.BRIEF)) {141dgcLog.log(Log.BRIEF, "dgcFilter = " + filter);142}143}144return filter;145}146147/**148* Construct a new server-side remote object collector at149* a particular port. Disallow construction from outside.150*/151private DGCImpl() {}152153/**154* The dirty call adds the VMID "vmid" to the set of clients155* that hold references to the object associated with the ObjID156* id. The long "sequenceNum" is used to detect late dirty calls. If157* the VMID "vmid" is null, a VMID will be generated on the158* server (for use by the client in subsequent calls) and159* returned.160*161* The client must call the "dirty" method to renew the lease162* before the "lease" time expires or all references to remote163* objects in this VM that the client holds are considered164* "unreferenced".165*/166public Lease dirty(ObjID[] ids, long sequenceNum, Lease lease) {167VMID vmid = lease.getVMID();168/*169* The server specifies the lease value; the client has170* no say in the matter.171*/172long duration = leaseValue;173174if (dgcLog.isLoggable(Log.VERBOSE)) {175dgcLog.log(Log.VERBOSE, "vmid = " + vmid);176}177178// create a VMID if one wasn't supplied179if (vmid == null) {180vmid = new VMID();181182if (dgcLog.isLoggable(Log.BRIEF)) {183String clientHost;184try {185clientHost = RemoteServer.getClientHost();186} catch (ServerNotActiveException e) {187clientHost = "<unknown host>";188}189dgcLog.log(Log.BRIEF, " assigning vmid " + vmid +190" to client " + clientHost);191}192}193194lease = new Lease(vmid, duration);195// record lease information196synchronized (leaseTable) {197LeaseInfo info = leaseTable.get(vmid);198if (info == null) {199leaseTable.put(vmid, new LeaseInfo(vmid, duration));200if (checker == null) {201checker = scheduler.scheduleWithFixedDelay(202new Runnable() {203public void run() {204checkLeases();205}206},207leaseCheckInterval,208leaseCheckInterval, TimeUnit.MILLISECONDS);209}210} else {211info.renew(duration);212}213}214215for (ObjID id : ids) {216if (dgcLog.isLoggable(Log.VERBOSE)) {217dgcLog.log(Log.VERBOSE, "id = " + id +218", vmid = " + vmid + ", duration = " + duration);219}220221ObjectTable.referenced(id, sequenceNum, vmid);222}223224// return the VMID used225return lease;226}227228/**229* The clean call removes the VMID from the set of clients230* that hold references to the object associated with the LiveRef231* ref. The sequence number is used to detect late clean calls. If the232* argument "strong" is true, then the clean call is a result of a233* failed "dirty" call, thus the sequence number for the VMID needs234* to be remembered until the client goes away.235*/236public void clean(ObjID[] ids, long sequenceNum, VMID vmid, boolean strong)237{238for (ObjID id : ids) {239if (dgcLog.isLoggable(Log.VERBOSE)) {240dgcLog.log(Log.VERBOSE, "id = " + id +241", vmid = " + vmid + ", strong = " + strong);242}243244ObjectTable.unreferenced(id, sequenceNum, vmid, strong);245}246}247248/**249* Register interest in receiving a callback when this VMID250* becomes inaccessible.251*/252void registerTarget(VMID vmid, Target target) {253synchronized (leaseTable) {254LeaseInfo info = leaseTable.get(vmid);255if (info == null) {256target.vmidDead(vmid);257} else {258info.notifySet.add(target);259}260}261}262263/**264* Remove notification request.265*/266void unregisterTarget(VMID vmid, Target target) {267synchronized (leaseTable) {268LeaseInfo info = leaseTable.get(vmid);269if (info != null) {270info.notifySet.remove(target);271}272}273}274275/**276* Check if leases have expired. If a lease has expired, remove277* it from the table and notify all interested parties that the278* VMID is essentially "dead".279*280* @return if true, there are leases outstanding; otherwise leases281* no longer need to be checked282*/283private void checkLeases() {284long time = System.currentTimeMillis();285286/* List of vmids that need to be removed from the leaseTable */287List<LeaseInfo> toUnregister = new ArrayList<>();288289/* Build a list of leaseInfo objects that need to have290* targets removed from their notifySet. Remove expired291* leases from leaseTable.292*/293synchronized (leaseTable) {294Iterator<LeaseInfo> iter = leaseTable.values().iterator();295while (iter.hasNext()) {296LeaseInfo info = iter.next();297if (info.expired(time)) {298toUnregister.add(info);299iter.remove();300}301}302303if (leaseTable.isEmpty()) {304checker.cancel(false);305checker = null;306}307}308309/* Notify and unegister targets without holding the lock on310* the leaseTable so we avoid deadlock.311*/312for (LeaseInfo info : toUnregister) {313for (Target target : info.notifySet) {314target.vmidDead(info.vmid);315}316}317}318319static {320/*321* "Export" the singleton DGCImpl in a context isolated from322* the arbitrary current thread context.323*/324AccessController.doPrivileged(new PrivilegedAction<Void>() {325public Void run() {326ClassLoader savedCcl =327Thread.currentThread().getContextClassLoader();328try {329Thread.currentThread().setContextClassLoader(330ClassLoader.getSystemClassLoader());331332/*333* Put remote collector object in table by hand to prevent334* listen on port. (UnicastServerRef.exportObject would335* cause transport to listen.)336*/337try {338dgc = new DGCImpl();339ObjID dgcID = new ObjID(ObjID.DGC_ID);340LiveRef ref = new LiveRef(dgcID, 0);341UnicastServerRef disp = new UnicastServerRef(ref,342DGCImpl::checkInput);343Remote stub =344Util.createProxy(DGCImpl.class,345new UnicastRef(ref), true);346disp.setSkeleton(dgc);347348Permissions perms = new Permissions();349perms.add(new SocketPermission("*", "accept,resolve"));350ProtectionDomain[] pd = { new ProtectionDomain(null, perms) };351AccessControlContext acceptAcc = new AccessControlContext(pd);352353Target target = AccessController.doPrivileged(354new PrivilegedAction<Target>() {355public Target run() {356return new Target(dgc, disp, stub, dgcID, true);357}358}, acceptAcc);359360ObjectTable.putTarget(target);361} catch (RemoteException e) {362throw new Error(363"exception initializing server-side DGC", e);364}365} finally {366Thread.currentThread().setContextClassLoader(savedCcl);367}368return null;369}370});371}372373/**374* ObjectInputFilter to filter DGC input objects.375* The list of acceptable classes is very short and explicit.376* The depth and array sizes are limited.377*378* @param filterInfo access to class, arrayLength, etc.379* @return {@link ObjectInputFilter.Status#ALLOWED} if allowed,380* {@link ObjectInputFilter.Status#REJECTED} if rejected,381* otherwise {@link ObjectInputFilter.Status#UNDECIDED}382*/383private static ObjectInputFilter.Status checkInput(ObjectInputFilter.FilterInfo filterInfo) {384if (dgcFilter != null) {385ObjectInputFilter.Status status = dgcFilter.checkInput(filterInfo);386if (status != ObjectInputFilter.Status.UNDECIDED) {387// The DGC filter can override the built-in white-list388return status;389}390}391392if (filterInfo.depth() > DGC_MAX_DEPTH) {393return ObjectInputFilter.Status.REJECTED;394}395Class<?> clazz = filterInfo.serialClass();396if (clazz != null) {397while (clazz.isArray()) {398if (filterInfo.arrayLength() >= 0 && filterInfo.arrayLength() > DGC_MAX_ARRAY_SIZE) {399return ObjectInputFilter.Status.REJECTED;400}401// Arrays are allowed depending on the component type402clazz = clazz.getComponentType();403}404if (clazz.isPrimitive()) {405// Arrays of primitives are allowed406return ObjectInputFilter.Status.ALLOWED;407}408return (clazz == ObjID.class ||409clazz == UID.class ||410clazz == VMID.class ||411clazz == Lease.class)412? ObjectInputFilter.Status.ALLOWED413: ObjectInputFilter.Status.REJECTED;414}415// Not a class, not size limited416return ObjectInputFilter.Status.UNDECIDED;417}418419420private static class LeaseInfo {421VMID vmid;422long expiration;423Set<Target> notifySet = new HashSet<>();424425LeaseInfo(VMID vmid, long lease) {426this.vmid = vmid;427expiration = System.currentTimeMillis() + lease;428}429430synchronized void renew(long lease) {431long newExpiration = System.currentTimeMillis() + lease;432if (newExpiration > expiration)433expiration = newExpiration;434}435436boolean expired(long time) {437if (expiration < time) {438if (dgcLog.isLoggable(Log.BRIEF)) {439dgcLog.log(Log.BRIEF, vmid.toString());440}441return true;442} else {443return false;444}445}446}447}448449450