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 37 import java.io.*; 38 import java.net.*; 39 import java.lang.ref.*; 40 41 import java.rmi.*; 42 import java.rmi.server.*; 43 44 interface ReturnRemote extends Remote { 45 Object returnRemote() throws RemoteException; 46 } 47 48 public 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 }