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