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