1 /*
2  * Copyright (c) 1998, 2016, 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 java.io.*;
30 import java.lang.reflect.*;
31 import java.rmi.dgc.*;
32 import java.util.*;
33 import java.rmi.registry.*;
34 import java.rmi.server.*;
35 
36 public class TestImpl extends RegistryRunner
37     implements Test {
38     static Thread locker = null;
39     static TestImpl foo = null;
40     static TestImpl bar = null;
41 
TestImpl()42     TestImpl() throws RemoteException {
43     }
44 
echo(String msg)45     public String echo(String msg) throws RemoteException {
46 
47         if (locker == null) {
48             // hold the target if not already held
49             locker = lockTargetExpireLeases(foo, DGCDeadLock.HOLD_TARGET_TIME);
50         }
51         return "Message received: " + msg;
52     }
53 
main(String[] args)54     static public void main(String[] args) {
55         try {
56             int registryPort = RegistryRunner.init(args);
57 
58             //export "Foo"
59             foo = new TestImpl();
60             Naming.rebind("rmi://:" +
61                           registryPort
62                           + "/Foo", foo);
63 
64             try {
65                 //export "Bar" after leases have been expired.
66                 bar = new TestImpl();
67                 Naming.rebind("rmi://localhost:" +
68                               registryPort
69                               + "/Bar", bar);
70             } catch (Exception e) {
71                 throw new RemoteException(e.getMessage());
72             }
73 
74             RegistryRunner.notify(registryPort);
75         } catch (Exception e) {
76             System.err.println(e.getMessage());
77             e.printStackTrace();
78         }
79     }
80 
lockTargetExpireLeases(Remote toLock, int timeOut)81     static Thread lockTargetExpireLeases(Remote toLock, int timeOut) {
82         Thread t = new Thread((Runnable) new TargetLocker(toLock, timeOut));
83         t.start();
84         return t;
85     }
86 
87     static class TargetLocker implements Runnable {
88 
89         Remote toLock = null;
90         int timeOut = 0;
91 
TargetLocker(Remote toLock, int timeOut)92         TargetLocker(Remote toLock, int timeOut) {
93             this.toLock = toLock;
94             this.timeOut = timeOut;
95         }
96 
run()97         public void run() {
98             try {
99                 // give dgc dirty calls time to finish.
100                 Thread.currentThread().sleep(4000);
101 
102                 java.security.AccessController.
103                     doPrivileged(new LockTargetCheckLeases(toLock,
104                                                            timeOut));
105 
106             } catch (Exception e) {
107                 System.err.println(e.getMessage());
108                 e.printStackTrace();
109                 System.exit(1);
110             }
111         }
112     }
113 
114     static class LockTargetCheckLeases
115         implements java.security.PrivilegedAction {
116 
117         Remote toLock = null;
118         int timeOut = 0;
119 
LockTargetCheckLeases(Remote toLock, int timeOut)120         LockTargetCheckLeases(Remote toLock, int timeOut) {
121             this.toLock = toLock;
122             this.timeOut = timeOut;
123         }
124 
run()125         public Object run() {
126             try {
127 
128                 Class args[] = new Class[1];
129 
130                 Class objTableClass = Class.forName
131                     ("sun.rmi.transport.ObjectTable");
132 
133                 /* get the Target that corresponds to toLock from the
134                  * ObjectTable
135                  */
136                 args[0] = Class.forName("java.rmi.Remote");
137                 Method objTableGetTarget =
138                     objTableClass.getDeclaredMethod("getTarget", args );
139                 objTableGetTarget.setAccessible(true);
140 
141                 Target lockTarget =
142                     ((Target) objTableGetTarget.invoke
143                      (null , new Object [] {toLock} ));
144 
145                 // make sure the lease on this object has expired.
146                 expireLeases(lockTarget);
147 
148                 // stop other threads from using the target for toLock.
149                 synchronized (lockTarget) {
150                     System.err.println("Locked the relevant target, sleeping " +
151                                        timeOut/1000 + " seconds");
152                     Thread.currentThread().sleep(timeOut);
153                     System.err.println("Target unlocked");
154                 }
155 
156             } catch (Exception e) {
157                 System.err.println(e.getMessage());
158                 e.printStackTrace();
159                 System.exit(1);
160             }
161             return null;
162         }
163     }
164 
165     /* leases have long values, so no dirty calls which would lock out
166      * a clean call, but the leases need to expire anyway, so we do it
167      * explicitly.
168      */
expireLeases(Target t)169     static void expireLeases(Target t) throws Exception {
170 
171         final Target target = t;
172 
173         java.security.AccessController.doPrivileged(
174 
175             //  put this into another class?
176             new java.security.PrivilegedAction() {
177             public Object run() {
178                 try {
179 
180                     Class DGCClass = Class.forName("sun.rmi.transport.DGCImpl");
181                     Method getDGCImpl =
182                         DGCClass.getDeclaredMethod("getDGCImpl", null );
183                     getDGCImpl.setAccessible(true);
184 
185                     // make sure the lease on this object has expired.
186                     DGC dgcImpl = ((DGC) getDGCImpl.invoke(null, null));
187 
188                     /* Get the lease table from the DGCImpl. */
189                     Field reflectedLeaseTable =
190                         dgcImpl.getClass().getDeclaredField("leaseTable");
191                     reflectedLeaseTable.setAccessible(true);
192 
193                     Map leaseTable = (Map) reflectedLeaseTable.get(dgcImpl);
194 
195                     // dont really need this synchronization...
196                     synchronized (leaseTable) {
197                         Iterator en = leaseTable.values().iterator();
198                         while (en.hasNext()) {
199                             Object info = en.next();
200 
201                             /* Get the notifySet in the leaseInfo object. */
202                             Field notifySetField =
203                                 info.getClass().getDeclaredField("notifySet");
204                             notifySetField.setAccessible(true);
205                             HashSet notifySet = (HashSet) notifySetField.get(info);
206 
207                             Iterator iter = notifySet.iterator();
208                             while (iter.hasNext()) {
209                                 Target notified = (Target) iter.next();
210 
211                                 if (notified == target) {
212 
213                                 /* Get and set the expiration field from the info object. */
214                                     Field expirationField = info.getClass().
215                                         getDeclaredField("expiration");
216                                     expirationField.setAccessible(true);
217                                     expirationField.setLong(info, 0);
218                                 }
219                             }
220                         }
221                     }
222                 } catch (Exception e) {
223                     System.err.println(e.getMessage());
224                     e.printStackTrace();
225                     System.exit(1);
226                 }
227                 // no interesting return value for this privileged action
228                 return null;
229             }
230         });
231     }
232 }
233