Path: blob/master/test/jdk/java/rmi/dgc/dgcAckFailure/DGCAckFailure.java
41153 views
/*1* Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/* @test24* @bug 4017232 804633925* @summary If, after returning a reference to a remote object in the current26* VM (which gets implicitly converted to a remote stub), the client fails to27* both send a DGC dirty call and to send a "DGC acknowledgment", the RMI28* runtime should eventually allow the remote object to be garbage collected,29* rather than pinning it indefinitely.30* @author Peter Jones31*32* @modules java.rmi/sun.rmi.transport:+open33* @build DGCAckFailure DGCAckFailure_Stub34* @run main/othervm DGCAckFailure35*/3637import java.io.*;38import java.net.*;39import java.lang.reflect.Field;40import java.lang.ref.*;4142import java.rmi.*;43import java.rmi.server.*;44import java.util.Map;4546import sun.rmi.transport.DGCAckHandler;4748interface ReturnRemote extends Remote {49Object returnRemote() throws RemoteException;50}5152public class DGCAckFailure implements ReturnRemote {5354private static final long TIMEOUT = 20000;55private static final long ACK_TIMEOUT = TIMEOUT / 2;5657public Object returnRemote() {58return new Wrapper(this);59}6061public static void main(String[] args) throws Exception {6263System.setProperty("sun.rmi.dgc.ackTimeout",64Long.toString(ACK_TIMEOUT));6566/*67* Set a socket factory that has a hook for shutting down all client68* output (writes from client-created sockets and new connection69* attempts). We then use this hook right before a remote stub gets70* deserialized, so that the client will not be able to send a DGC71* dirty call, or a DGC acknowledgment. Without the DGC ack, we72* hope that the RMI runtime will still eventually allow the remote73* object to be garbage collected.74*/75RMISocketFactory.setSocketFactory(new TestSF());76System.err.println("test socket factory set");7778Remote impl = new DGCAckFailure();79ReferenceQueue refQueue = new ReferenceQueue();80Reference weakRef = new WeakReference(impl, refQueue);81ReturnRemote stub =82(ReturnRemote) UnicastRemoteObject.exportObject(impl);83System.err.println("remote object exported; stub = " + stub);8485try {86Object wrappedStub = stub.returnRemote();87System.err.println("invocation returned: " + wrappedStub);8889impl = null;90stub = null; // in case 4114579 ever gets fixed91System.err.println("strong references to impl cleared");9293System.err.println("waiting for weak reference notification:");94Reference ref = null;95for (int i = 0; i < 6; i++) {96System.gc();97ref = refQueue.remove(TIMEOUT / 5);98if (ref != null) {99break;100}101}102if (ref != weakRef) {103throw new RuntimeException("TEST FAILED: " +104"timed out, remote object not garbage collected");105}106107// 8046339108// All DGCAckHandlers must be properly released after timeout109Thread.sleep(ACK_TIMEOUT + 100);110try {111Field field =112DGCAckHandler.class.getDeclaredField("idTable");113field.setAccessible(true);114Object obj = field.get(null);115Map<?,?> idTable = (Map<?,?>)obj;116117if (!idTable.isEmpty()) {118throw new RuntimeException("TEST FAILED: " +119"DGCAckHandler.idTable isn't empty");120}121} catch (ReflectiveOperationException roe) {122throw new RuntimeException(roe);123}124125System.err.println("TEST PASSED");126127} finally {128try {129UnicastRemoteObject.unexportObject((Remote) weakRef.get(),130true);131} catch (Exception e) {132}133}134}135136private static class Wrapper implements Serializable {137private final Remote obj;138Wrapper(Remote obj) { this.obj = obj; }139140private void readObject(ObjectInputStream in)141throws IOException, ClassNotFoundException142{143TestSF.shutdownClientOutput();144System.err.println(145"Wrapper.readObject: SHUTTING DOWN CLIENT OUTPUT");146in.defaultReadObject();147}148149public String toString() { return "Wrapper[" + obj + "]"; }150}151152private static class TestSF extends RMISocketFactory {153154private static volatile boolean shutdown = false;155static void shutdownClientOutput() { shutdown = true; }156157public Socket createSocket(String host, int port) throws IOException {158if (shutdown) {159IOException e = new java.net.ConnectException(160"test socket factory rejecting client connection");161System.err.println(e);162// e.printStackTrace();163throw e;164} else {165return new TestSocket(host, port);166}167}168169public ServerSocket createServerSocket(int port) throws IOException {170return new ServerSocket(port);171}172173private static class TestSocket extends Socket {174TestSocket(String host, int port) throws IOException {175super(host, port);176}177public OutputStream getOutputStream() throws IOException {178return new TestOutputStream(super.getOutputStream());179}180}181182private static class TestOutputStream extends FilterOutputStream {183TestOutputStream(OutputStream out) { super(out); }184public void write(int b) throws IOException {185if (shutdown) {186IOException e = new IOException(187"connection broken by test socket factory");188System.err.println(e);189// e.printStackTrace();190throw e;191} else {192super.write(b);193}194}195}196}197}198199200