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