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