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