1 /* 2 * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package sun.rmi.transport; 26 27 import java.rmi.Remote; 28 import java.rmi.NoSuchObjectException; 29 import java.rmi.dgc.VMID; 30 import java.rmi.server.ObjID; 31 import java.rmi.server.Unreferenced; 32 import java.security.AccessControlContext; 33 import java.security.AccessController; 34 import java.security.PrivilegedAction; 35 import java.util.*; 36 import sun.rmi.runtime.Log; 37 import sun.rmi.runtime.NewThreadAction; 38 import sun.rmi.server.Dispatcher; 39 40 /** 41 * A target contains information pertaining to a remote object that 42 * resides in this address space. Targets are located via the 43 * ObjectTable. 44 */ 45 public final class Target { 46 /** object id for target */ 47 private final ObjID id; 48 /** flag indicating whether target is subject to collection */ 49 private final boolean permanent; 50 /** weak reference to remote object implementation */ 51 private final WeakRef weakImpl; 52 /** dispatcher for remote object */ 53 private volatile Dispatcher disp; 54 /** stub for remote object */ 55 private final Remote stub; 56 /** set of clients that hold references to this target */ 57 private final Vector<VMID> refSet = new Vector<>(); 58 /** table that maps client endpoints to sequence numbers */ 59 private final Hashtable<VMID, SequenceEntry> sequenceTable = 60 new Hashtable<>(5); 61 /** access control context in which target was created */ 62 private final AccessControlContext acc; 63 /** context class loader in which target was created */ 64 private final ClassLoader ccl; 65 /** number of pending/executing calls */ 66 private int callCount = 0; 67 /** true if this target has been removed from the object table */ 68 private boolean removed = false; 69 /** 70 * the transport through which this target was exported and 71 * through which remote calls will be allowed 72 */ 73 private volatile Transport exportedTransport = null; 74 75 /** number to identify next callback thread created here */ 76 private static int nextThreadNum = 0; 77 78 /** 79 * Construct a Target for a remote object "impl" with 80 * a specific object id. 81 * 82 * If "permanent" is true, then the impl is pinned permanently 83 * (the impl will not be collected via distributed and/or local 84 * GC). If "on" is false, than the impl is subject to 85 * collection. Permanent objects do not keep a server from 86 * exiting. 87 */ 88 public Target(Remote impl, Dispatcher disp, Remote stub, ObjID id, 89 boolean permanent) 90 { 91 this.weakImpl = new WeakRef(impl, ObjectTable.reapQueue); 92 this.disp = disp; 93 this.stub = stub; 94 this.id = id; 95 this.acc = AccessController.getContext(); 96 97 /* 98 * Fix for 4149366: so that downloaded parameter types unmarshalled 99 * for this impl will be compatible with types known only to the 100 * impl class's class loader (when it's not identical to the 101 * exporting thread's context class loader), mark the impl's class 102 * loader as the loader to use as the context class loader in the 103 * server's dispatch thread while a call to this impl is being 104 * processed (unless this exporting thread's context class loader is 105 * a child of the impl's class loader, such as when a registry is 106 * exported by an application, in which case this thread's context 107 * class loader is preferred). 108 */ 109 ClassLoader threadContextLoader = 110 Thread.currentThread().getContextClassLoader(); 111 ClassLoader serverLoader = impl.getClass().getClassLoader(); 112 if (checkLoaderAncestry(threadContextLoader, serverLoader)) { 113 this.ccl = threadContextLoader; 114 } else { 115 this.ccl = serverLoader; 116 } 117 118 this.permanent = permanent; 119 if (permanent) { 120 pinImpl(); 121 } 122 } 123 124 /** 125 * Return true if the first class loader is a child of (or identical 126 * to) the second class loader. Either loader may be "null", which is 127 * considered to be the parent of any non-null class loader. 128 * 129 * (utility method added for the 1.2beta4 fix for 4149366) 130 */ 131 private static boolean checkLoaderAncestry(ClassLoader child, 132 ClassLoader ancestor) 133 { 134 if (ancestor == null) { 135 return true; 136 } else if (child == null) { 137 return false; 138 } else { 139 for (ClassLoader parent = child; 140 parent != null; 141 parent = parent.getParent()) 142 { 143 if (parent == ancestor) { 144 return true; 145 } 146 } 147 return false; 148 } 149 } 150 151 /** Get the stub (proxy) object for this target 152 */ 153 public Remote getStub() { 154 return stub; 155 } 156 157 /** 158 * Returns the object endpoint for the target. 159 */ 160 ObjectEndpoint getObjectEndpoint() { 161 return new ObjectEndpoint(id, exportedTransport); 162 } 163 164 /** 165 * Get the weak reference for the Impl of this target. 166 */ 167 WeakRef getWeakImpl() { 168 return weakImpl; 169 } 170 171 /** 172 * Returns the dispatcher for this remote object target. 173 */ 174 Dispatcher getDispatcher() { 175 return disp; 176 } 177 178 AccessControlContext getAccessControlContext() { 179 return acc; 180 } 181 182 ClassLoader getContextClassLoader() { 183 return ccl; 184 } 185 186 /** 187 * Get the impl for this target. 188 * Note: this may return null if the impl has been garbage collected. 189 * (currently, there is no need to make this method public) 190 */ 191 Remote getImpl() { 192 return (Remote)weakImpl.get(); 193 } 194 195 /** 196 * Returns true if the target is permanent. 197 */ 198 boolean isPermanent() { 199 return permanent; 200 } 201 202 /** 203 * Pin impl in target. Pin the WeakRef object so it holds a strong 204 * reference to the object to it will not be garbage collected locally. 205 * This way there is a single object responsible for the weak ref 206 * mechanism. 207 */ 208 synchronized void pinImpl() { 209 weakImpl.pin(); 210 } 211 212 /** 213 * Unpin impl in target. Weaken the reference to impl so that it 214 * can be garbage collected locally. But only if there the refSet 215 * is empty. All of the weak/strong handling is in WeakRef 216 */ 217 synchronized void unpinImpl() { 218 /* only unpin if: 219 * a) impl is not permanent, and 220 * b) impl is not already unpinned, and 221 * c) there are no external references (outside this 222 * address space) for the impl 223 */ 224 if (!permanent && refSet.isEmpty()) { 225 weakImpl.unpin(); 226 } 227 } 228 229 /** 230 * Enable the transport through which remote calls to this target 231 * are allowed to be set if it has not already been set. 232 */ 233 void setExportedTransport(Transport exportedTransport) { 234 if (this.exportedTransport == null) { 235 this.exportedTransport = exportedTransport; 236 } 237 } 238 239 /** 240 * Add an endpoint to the remembered set. Also adds a notifier 241 * to call back if the address space associated with the endpoint 242 * dies. 243 */ 244 synchronized void referenced(long sequenceNum, VMID vmid) { 245 // check sequence number for vmid 246 SequenceEntry entry = sequenceTable.get(vmid); 247 if (entry == null) { 248 sequenceTable.put(vmid, new SequenceEntry(sequenceNum)); 249 } else if (entry.sequenceNum < sequenceNum) { 250 entry.update(sequenceNum); 251 } else { 252 // late dirty call; ignore. 253 return; 254 } 255 256 if (!refSet.contains(vmid)) { 257 /* 258 * A Target must be pinned while its refSet is not empty. It may 259 * have become unpinned if external LiveRefs only existed in 260 * serialized form for some period of time, or if a client failed 261 * to renew its lease due to a transient network failure. So, 262 * make sure that it is pinned here; this fixes bugid 4069644. 263 */ 264 pinImpl(); 265 if (getImpl() == null) // too late if impl was collected 266 return; 267 268 if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) { 269 DGCImpl.dgcLog.log(Log.VERBOSE, "add to dirty set: " + vmid); 270 } 271 272 refSet.addElement(vmid); 273 274 DGCImpl.getDGCImpl().registerTarget(vmid, this); 275 } 276 } 277 278 /** 279 * Remove endpoint from remembered set. If set becomes empty, 280 * remove server from Transport's object table. 281 */ 282 synchronized void unreferenced(long sequenceNum, VMID vmid, boolean strong) 283 { 284 // check sequence number for vmid 285 SequenceEntry entry = sequenceTable.get(vmid); 286 if (entry == null || entry.sequenceNum > sequenceNum) { 287 // late clean call; ignore 288 return; 289 } else if (strong) { 290 // strong clean call; retain sequenceNum 291 entry.retain(sequenceNum); 292 } else if (entry.keep == false) { 293 // get rid of sequence number 294 sequenceTable.remove(vmid); 295 } 296 297 if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) { 298 DGCImpl.dgcLog.log(Log.VERBOSE, "remove from dirty set: " + vmid); 299 } 300 301 refSetRemove(vmid); 302 } 303 304 /** 305 * Remove endpoint from the reference set. 306 */ 307 synchronized private void refSetRemove(VMID vmid) { 308 // remove notification request 309 DGCImpl.getDGCImpl().unregisterTarget(vmid, this); 310 311 if (refSet.removeElement(vmid) && refSet.isEmpty()) { 312 // reference set is empty, so server can be garbage collected. 313 // remove object from table. 314 if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) { 315 DGCImpl.dgcLog.log(Log.VERBOSE, 316 "reference set is empty: target = " + this); 317 } 318 319 /* 320 * If the remote object implements the Unreferenced interface, 321 * invoke its unreferenced callback in a separate thread. 322 */ 323 Remote obj = getImpl(); 324 if (obj instanceof Unreferenced) { 325 final Unreferenced unrefObj = (Unreferenced) obj; 326 AccessController.doPrivileged( 327 new NewThreadAction(() -> { 328 Thread.currentThread().setContextClassLoader(ccl); 329 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 330 unrefObj.unreferenced(); 331 return null; 332 }, acc); 333 }, "Unreferenced-" + nextThreadNum++, false, true)).start(); 334 // REMIND: access to nextThreadNum not synchronized; you care? 335 } 336 337 unpinImpl(); 338 } 339 } 340 341 /** 342 * Mark this target as not accepting new calls if any of the 343 * following conditions exist: a) the force parameter is true, 344 * b) the target's call count is zero, or c) the object is already 345 * not accepting calls. Returns true if target is marked as not 346 * accepting new calls; returns false otherwise. 347 */ 348 synchronized boolean unexport(boolean force) { 349 350 if ((force == true) || (callCount == 0) || (disp == null)) { 351 disp = null; 352 /* 353 * Fix for 4331349: unpin object so that it may be gc'd. 354 * Also, unregister all vmids referencing this target 355 * so target can be gc'd. 356 */ 357 unpinImpl(); 358 DGCImpl dgc = DGCImpl.getDGCImpl(); 359 Enumeration<VMID> enum_ = refSet.elements(); 360 while (enum_.hasMoreElements()) { 361 VMID vmid = enum_.nextElement(); 362 dgc.unregisterTarget(vmid, this); 363 } 364 return true; 365 } else { 366 return false; 367 } 368 } 369 370 /** 371 * Mark this target as having been removed from the object table. 372 */ 373 synchronized void markRemoved() { 374 if (!(!removed)) { throw new AssertionError(); } 375 376 removed = true; 377 if (!permanent && callCount == 0) { 378 ObjectTable.decrementKeepAliveCount(); 379 } 380 381 if (exportedTransport != null) { 382 exportedTransport.targetUnexported(); 383 } 384 } 385 386 /** 387 * Increment call count. 388 */ 389 synchronized void incrementCallCount() throws NoSuchObjectException { 390 391 if (disp != null) { 392 callCount ++; 393 } else { 394 throw new NoSuchObjectException("object not accepting new calls"); 395 } 396 } 397 398 /** 399 * Decrement call count. 400 */ 401 synchronized void decrementCallCount() { 402 403 if (--callCount < 0) { 404 throw new Error("internal error: call count less than zero"); 405 } 406 407 /* 408 * The "keep-alive count" is the number of non-permanent remote 409 * objects that are either in the object table or still have calls 410 * in progress. Therefore, this state change may affect the 411 * keep-alive count: if this target is for a non-permanent remote 412 * object that has been removed from the object table and now has a 413 * call count of zero, it needs to be decremented. 414 */ 415 if (!permanent && removed && callCount == 0) { 416 ObjectTable.decrementKeepAliveCount(); 417 } 418 } 419 420 /** 421 * Returns true if remembered set is empty; otherwise returns 422 * false 423 */ 424 boolean isEmpty() { 425 return refSet.isEmpty(); 426 } 427 428 /** 429 * This method is called if the address space associated with the 430 * vmid dies. In that case, the vmid should be removed 431 * from the reference set. 432 */ 433 synchronized public void vmidDead(VMID vmid) { 434 if (DGCImpl.dgcLog.isLoggable(Log.BRIEF)) { 435 DGCImpl.dgcLog.log(Log.BRIEF, "removing endpoint " + 436 vmid + " from reference set"); 437 } 438 439 sequenceTable.remove(vmid); 440 refSetRemove(vmid); 441 } 442 } 443 444 class SequenceEntry { 445 long sequenceNum; 446 boolean keep; 447 448 SequenceEntry(long sequenceNum) { 449 this.sequenceNum = sequenceNum; 450 keep = false; 451 } 452 453 void retain(long sequenceNum) { 454 this.sequenceNum = sequenceNum; 455 keep = true; 456 } 457 458 void update(long sequenceNum) { 459 this.sequenceNum = sequenceNum; 460 } 461 }