1 /*
2  * Copyright (c) 2001, 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 /* @test
25  * @bug 4017232 8046339
26  * @summary If, after returning a reference to a remote object in the current
27  * VM (which gets implicitly converted to a remote stub), the client fails to
28  * both send a DGC dirty call and to send a "DGC acknowledgment", the RMI
29  * runtime should eventually allow the remote object to be garbage collected,
30  * rather than pinning it indefinitely.
31  * @author Peter Jones
32  *
33  * @modules java.rmi/sun.rmi.transport:+open
34  * @build DGCAckFailure DGCAckFailure_Stub
35  * @run main/othervm DGCAckFailure
36  */
37 
38 import java.io.*;
39 import java.net.*;
40 import java.lang.reflect.Field;
41 import java.lang.ref.*;
42 
43 import java.rmi.*;
44 import java.rmi.server.*;
45 import java.util.Map;
46 
47 import sun.rmi.transport.DGCAckHandler;
48 
49 interface ReturnRemote extends Remote {
returnRemote()50     Object returnRemote() throws RemoteException;
51 }
52 
53 public class DGCAckFailure implements ReturnRemote {
54 
55     private static final long TIMEOUT = 20000;
56     private static final long ACK_TIMEOUT = TIMEOUT / 2;
57 
returnRemote()58     public Object returnRemote() {
59         return new Wrapper(this);
60     }
61 
main(String[] args)62     public static void main(String[] args) throws Exception {
63 
64         System.setProperty("sun.rmi.dgc.ackTimeout",
65                 Long.toString(ACK_TIMEOUT));
66 
67         /*
68          * Set a socket factory that has a hook for shutting down all client
69          * output (writes from client-created sockets and new connection
70          * attempts).  We then use this hook right before a remote stub gets
71          * deserialized, so that the client will not be able to send a DGC
72          * dirty call, or a DGC acknowledgment.  Without the DGC ack, we
73          * hope that the RMI runtime will still eventually allow the remote
74          * object to be garbage collected.
75          */
76         RMISocketFactory.setSocketFactory(new TestSF());
77         System.err.println("test socket factory set");
78 
79         Remote impl = new DGCAckFailure();
80         ReferenceQueue refQueue = new ReferenceQueue();
81         Reference weakRef = new WeakReference(impl, refQueue);
82         ReturnRemote stub =
83             (ReturnRemote) UnicastRemoteObject.exportObject(impl);
84         System.err.println("remote object exported; stub = " + stub);
85 
86         try {
87             Object wrappedStub = stub.returnRemote();
88             System.err.println("invocation returned: " + wrappedStub);
89 
90             impl = null;
91             stub = null;        // in case 4114579 ever gets fixed
92             System.err.println("strong references to impl cleared");
93 
94             System.err.println("waiting for weak reference notification:");
95             Reference ref = null;
96             for (int i = 0; i < 6; i++) {
97                 System.gc();
98                 ref = refQueue.remove(TIMEOUT / 5);
99                 if (ref != null) {
100                     break;
101                 }
102             }
103             if (ref != weakRef) {
104                 throw new RuntimeException("TEST FAILED: " +
105                     "timed out, remote object not garbage collected");
106             }
107 
108             // 8046339
109             // All DGCAckHandlers must be properly released after timeout
110             Thread.sleep(ACK_TIMEOUT + 100);
111             try {
112                 Field field =
113                         DGCAckHandler.class.getDeclaredField("idTable");
114                 field.setAccessible(true);
115                 Object obj = field.get(null);
116                 Map<?,?> idTable = (Map<?,?>)obj;
117 
118                 if (!idTable.isEmpty()) {
119                     throw new RuntimeException("TEST FAILED: " +
120                             "DGCAckHandler.idTable isn't empty");
121                 }
122             } catch (ReflectiveOperationException roe) {
123                 throw new RuntimeException(roe);
124             }
125 
126             System.err.println("TEST PASSED");
127 
128         } finally {
129             try {
130                 UnicastRemoteObject.unexportObject((Remote) weakRef.get(),
131                                                    true);
132             } catch (Exception e) {
133             }
134         }
135     }
136 
137     private static class Wrapper implements Serializable {
138         private final Remote obj;
Wrapper(Remote obj)139         Wrapper(Remote obj) { this.obj = obj; }
140 
readObject(ObjectInputStream in)141         private void readObject(ObjectInputStream in)
142             throws IOException, ClassNotFoundException
143         {
144             TestSF.shutdownClientOutput();
145             System.err.println(
146                 "Wrapper.readObject: SHUTTING DOWN CLIENT OUTPUT");
147             in.defaultReadObject();
148         }
149 
toString()150         public String toString() { return "Wrapper[" + obj + "]"; }
151     }
152 
153     private static class TestSF extends RMISocketFactory {
154 
155         private static volatile boolean shutdown = false;
shutdownClientOutput()156         static void shutdownClientOutput() { shutdown = true; }
157 
createSocket(String host, int port)158         public Socket createSocket(String host, int port) throws IOException {
159             if (shutdown) {
160                 IOException e = new java.net.ConnectException(
161                     "test socket factory rejecting client connection");
162                 System.err.println(e);
163 //              e.printStackTrace();
164                 throw e;
165             } else {
166                 return new TestSocket(host, port);
167             }
168         }
169 
createServerSocket(int port)170         public ServerSocket createServerSocket(int port) throws IOException {
171             return new ServerSocket(port);
172         }
173 
174         private static class TestSocket extends Socket {
TestSocket(String host, int port)175             TestSocket(String host, int port) throws IOException {
176                 super(host, port);
177             }
getOutputStream()178             public OutputStream getOutputStream() throws IOException {
179                 return new TestOutputStream(super.getOutputStream());
180             }
181         }
182 
183         private static class TestOutputStream extends FilterOutputStream {
TestOutputStream(OutputStream out)184             TestOutputStream(OutputStream out) { super(out); }
write(int b)185             public void write(int b) throws IOException {
186                 if (shutdown) {
187                     IOException e = new IOException(
188                         "connection broken by test socket factory");
189                     System.err.println(e);
190 //                  e.printStackTrace();
191                     throw e;
192                 } else {
193                     super.write(b);
194                 }
195             }
196         }
197     }
198 }
199