blob: 446b2405ccf77571f1a2d436e7411ce32e17c1b7 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2001 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24/* @test
25 * @bug 4017232
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 * @build DGCAckFailure
34 * @build DGCAckFailure_Stub
35 * @run main/othervm DGCAckFailure
36 */
37
38import java.io.*;
39import java.net.*;
40import java.lang.ref.*;
41
42import java.rmi.*;
43import java.rmi.server.*;
44
45interface ReturnRemote extends Remote {
46 Object returnRemote() throws RemoteException;
47}
48
49public class DGCAckFailure implements ReturnRemote {
50
51 private static final long TIMEOUT = 20000;
52
53 public Object returnRemote() {
54 return new Wrapper(this);
55 }
56
57 public static void main(String[] args) throws Exception {
58
59 System.setProperty("sun.rmi.dgc.ackTimeout", "10000");
60
61 /*
62 * Set a socket factory that has a hook for shutting down all client
63 * output (writes from client-created sockets and new connection
64 * attempts). We then use this hook right before a remote stub gets
65 * deserialized, so that the client will not be able to send a DGC
66 * dirty call, or a DGC acknowledgment. Without the DGC ack, we
67 * hope that the RMI runtime will still eventually allow the remote
68 * object to be garbage collected.
69 */
70 RMISocketFactory.setSocketFactory(new TestSF());
71 System.err.println("test socket factory set");
72
73 Remote impl = new DGCAckFailure();
74 ReferenceQueue refQueue = new ReferenceQueue();
75 Reference weakRef = new WeakReference(impl, refQueue);
76 ReturnRemote stub =
77 (ReturnRemote) UnicastRemoteObject.exportObject(impl);
78 System.err.println("remote object exported; stub = " + stub);
79
80 try {
81 Object wrappedStub = stub.returnRemote();
82 System.err.println("invocation returned: " + wrappedStub);
83
84 impl = null;
85 stub = null; // in case 4114579 ever gets fixed
86 System.err.println("strong references to impl cleared");
87
88 System.err.println("waiting for weak reference notification:");
89 Reference ref = null;
90 for (int i = 0; i < 6; i++) {
91 System.gc();
92 ref = refQueue.remove(TIMEOUT / 5);
93 if (ref != null) {
94 break;
95 }
96 }
97 if (ref == weakRef) {
98 System.err.println("TEST PASSED");
99 } else {
100 throw new RuntimeException("TEST FAILED: " +
101 "timed out, remote object not garbage collected");
102 }
103 } finally {
104 try {
105 UnicastRemoteObject.unexportObject((Remote) weakRef.get(),
106 true);
107 } catch (Exception e) {
108 }
109 }
110 }
111
112 private static class Wrapper implements Serializable {
113 private final Remote obj;
114 Wrapper(Remote obj) { this.obj = obj; }
115
116 private void readObject(ObjectInputStream in)
117 throws IOException, ClassNotFoundException
118 {
119 TestSF.shutdownClientOutput();
120 System.err.println(
121 "Wrapper.readObject: SHUTTING DOWN CLIENT OUTPUT");
122 in.defaultReadObject();
123 }
124
125 public String toString() { return "Wrapper[" + obj + "]"; }
126 }
127
128 private static class TestSF extends RMISocketFactory {
129
130 private static volatile boolean shutdown = false;
131 static void shutdownClientOutput() { shutdown = true; }
132
133 public Socket createSocket(String host, int port) throws IOException {
134 if (shutdown) {
135 IOException e = new java.net.ConnectException(
136 "test socket factory rejecting client connection");
137 System.err.println(e);
138// e.printStackTrace();
139 throw e;
140 } else {
141 return new TestSocket(host, port);
142 }
143 }
144
145 public ServerSocket createServerSocket(int port) throws IOException {
146 return new ServerSocket(port);
147 }
148
149 private static class TestSocket extends Socket {
150 TestSocket(String host, int port) throws IOException {
151 super(host, port);
152 }
153 public OutputStream getOutputStream() throws IOException {
154 return new TestOutputStream(super.getOutputStream());
155 }
156 }
157
158 private static class TestOutputStream extends FilterOutputStream {
159 TestOutputStream(OutputStream out) { super(out); }
160 public void write(int b) throws IOException {
161 if (shutdown) {
162 IOException e = new IOException(
163 "connection broken by test socket factory");
164 System.err.println(e);
165// e.printStackTrace();
166 throw e;
167 } else {
168 super.write(b);
169 }
170 }
171 }
172 }
173}