1 /*
   2  * Copyright (c) 1998, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  */
  26 
  27 import java.rmi.*;
  28 import sun.rmi.transport.*;
  29 import sun.rmi.*;
  30 import java.io.*;
  31 import java.lang.reflect.*;
  32 import java.rmi.dgc.*;
  33 import java.util.*;
  34 import java.rmi.registry.*;
  35 import java.rmi.server.*;
  36 
  37 public class TestImpl extends UnicastRemoteObject
  38     implements Test {
  39 
  40     static Thread locker = null;
  41     static TestImpl foo = null;
  42     static TestImpl bar = null;
  43 
  44     TestImpl() throws RemoteException {
  45     }
  46 
  47     public String echo(String msg) throws RemoteException {
  48 
  49         if (locker == null) {
  50             // hold the target if not already held
  51             locker = lockTargetExpireLeases(foo, DGCDeadLock.HOLD_TARGET_TIME);
  52         }
  53         return "Message received: " + msg;
  54     }
  55 
  56     static public void main(String[] args) {
  57         Registry registry = null;
  58 
  59         try {
  60             registry = java.rmi.registry.LocateRegistry.
  61                 createRegistry(TestLibrary.REGISTRY_PORT);
  62 
  63             //export "Foo"
  64             foo = new TestImpl();
  65             Naming.rebind("rmi://:" +
  66                           TestLibrary.REGISTRY_PORT
  67                           + "/Foo", foo);
  68 
  69             try {
  70                 //export "Bar" after leases have been expired.
  71                 bar = new TestImpl();
  72                 Naming.rebind("rmi://localhost:" +
  73                               TestLibrary.REGISTRY_PORT
  74                               + "/Bar", bar);
  75             } catch (Exception e) {
  76                 throw new RemoteException(e.getMessage());
  77             }
  78             Thread.sleep(DGCDeadLock.TEST_FAIL_TIME);
  79             System.err.println("object vm exiting...");
  80             System.exit(0);
  81 
  82         } catch (Exception e) {
  83             System.err.println(e.getMessage());
  84             e.printStackTrace();
  85         } finally {
  86             TestLibrary.unexport(registry);
  87             registry = null;
  88         }
  89     }
  90 
  91     static Thread lockTargetExpireLeases(Remote toLock, int timeOut) {
  92         Thread t = new Thread((Runnable) new TargetLocker(toLock, timeOut));
  93         t.start();
  94         return t;
  95     }
  96 
  97     static class TargetLocker implements Runnable {
  98 
  99         Remote toLock = null;
 100         int timeOut = 0;
 101 
 102         TargetLocker(Remote toLock, int timeOut) {
 103             this.toLock = toLock;
 104             this.timeOut = timeOut;
 105         }
 106 
 107         public void run() {
 108             try {
 109                 // give dgc dirty calls time to finish.
 110                 Thread.currentThread().sleep(4000);
 111 
 112                 java.security.AccessController.
 113                     doPrivileged(new LockTargetCheckLeases(toLock,
 114                                                            timeOut));
 115 
 116             } catch (Exception e) {
 117                 System.err.println(e.getMessage());
 118                 e.printStackTrace();
 119                 System.exit(1);
 120             }
 121         }
 122     }
 123 
 124     static class LockTargetCheckLeases
 125         implements java.security.PrivilegedAction {
 126 
 127         Remote toLock = null;
 128         int timeOut = 0;
 129 
 130         LockTargetCheckLeases(Remote toLock, int timeOut) {
 131             this.toLock = toLock;
 132             this.timeOut = timeOut;
 133         }
 134 
 135         public Object run() {
 136             try {
 137 
 138                 Class args[] = new Class[1];
 139 
 140                 Class objTableClass = Class.forName
 141                     ("sun.rmi.transport.ObjectTable");
 142 
 143                 /* get the Target that corresponds to toLock from the
 144                  * ObjectTable
 145                  */
 146                 args[0] = Class.forName("java.rmi.Remote");
 147                 Method objTableGetTarget =
 148                     objTableClass.getDeclaredMethod("getTarget", args );
 149                 objTableGetTarget.setAccessible(true);
 150 
 151                 Target lockTarget =
 152                     ((Target) objTableGetTarget.invoke
 153                      (null , new Object [] {toLock} ));
 154 
 155                 // make sure the lease on this object has expired.
 156                 expireLeases(lockTarget);
 157 
 158                 // stop other threads from using the target for toLock.
 159                 synchronized (lockTarget) {
 160                     System.err.println("Locked the relevant target, sleeping " +
 161                                        timeOut/1000 + " seconds");
 162                     Thread.currentThread().sleep(timeOut);
 163                     System.err.println("Target unlocked");
 164                 }
 165 
 166             } catch (Exception e) {
 167                 System.err.println(e.getMessage());
 168                 e.printStackTrace();
 169                 System.exit(1);
 170             }
 171             return null;
 172         }
 173     }
 174 
 175     /* leases have long values, so no dirty calls which would lock out
 176      * a clean call, but the leases need to expire anyway, so we do it
 177      * explicitly.
 178      */
 179     static void expireLeases(Target t) throws Exception {
 180 
 181         final Target target = t;
 182 
 183         java.security.AccessController.doPrivileged(
 184 
 185             //  put this into another class?
 186             new java.security.PrivilegedAction() {
 187             public Object run() {
 188                 try {
 189 
 190                     Class DGCClass = Class.forName("sun.rmi.transport.DGCImpl");
 191                     Method getDGCImpl =
 192                         DGCClass.getDeclaredMethod("getDGCImpl", null );
 193                     getDGCImpl.setAccessible(true);
 194 
 195                     // make sure the lease on this object has expired.
 196                     DGC dgcImpl = ((DGC) getDGCImpl.invoke(null, null));
 197 
 198                     /* Get the lease table from the DGCImpl. */
 199                     Field reflectedLeaseTable =
 200                         dgcImpl.getClass().getDeclaredField("leaseTable");
 201                     reflectedLeaseTable.setAccessible(true);
 202 
 203                     Map leaseTable = (Map) reflectedLeaseTable.get(dgcImpl);
 204 
 205                     // dont really need this synchronization...
 206                     synchronized (leaseTable) {
 207                         Iterator en = leaseTable.values().iterator();
 208                         while (en.hasNext()) {
 209                             Object info = en.next();
 210 
 211                             /* Get the notifySet in the leaseInfo object. */
 212                             Field notifySetField =
 213                                 info.getClass().getDeclaredField("notifySet");
 214                             notifySetField.setAccessible(true);
 215                             HashSet notifySet = (HashSet) notifySetField.get(info);
 216 
 217                             Iterator iter = notifySet.iterator();
 218                             while (iter.hasNext()) {
 219                                 Target notified = (Target) iter.next();
 220 
 221                                 if (notified == target) {
 222 
 223                                 /* Get and set the expiration field from the info object. */
 224                                     Field expirationField = info.getClass().
 225                                         getDeclaredField("expiration");
 226                                     expirationField.setAccessible(true);
 227                                     expirationField.setLong(info, 0);
 228                                 }
 229                             }
 230                         }
 231                     }
 232                 } catch (Exception e) {
 233                     System.err.println(e.getMessage());
 234                     e.printStackTrace();
 235                     System.exit(1);
 236                 }
 237                 // no interesting return value for this privileged action
 238                 return null;
 239             }
 240         });
 241     }
 242 }