jdk/test/java/rmi/dgc/retryDirtyCalls/RetryDirtyCalls.java
author duke
Sat, 01 Dec 2007 00:00:00 +0000
changeset 2 90ce3da70b43
child 309 bda219d843f6
permissions -rw-r--r--
Initial load

/* 
 * Copyright 1999 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

/* @test
 * @bug 4268258
 * @summary When a DGC dirty call fails, RMI's client-side DGC implementation
 * should attempt to retry the same dirty call a few times, at least until the
 * known lease for that endpoint has expired, instead of just giving up
 * renewing that lease at all after the first failure.
 * @author Peter Jones (inspired by Adrian Colley's test case in 4268258)
 *
 * @build RetryDirtyCalls
 * @build RetryDirtyCalls_Stub
 * @run main/othervm RetryDirtyCalls
 */

import java.io.*;
import java.net.*;
import java.rmi.*;
import java.rmi.server.*;

interface Self extends Remote {
    Self getSelf() throws RemoteException;
}

public class RetryDirtyCalls implements Self, Unreferenced {

    /** how long we wait before declaring that this test has passed */
    private final static long TIMEOUT = 20000;

    /** true if this object's unreferenced method has been called */
    private boolean unreferenced = false;

    /**
     * Return this object.  The need for this method is explained below.
     */
    public Self getSelf() {
	return this;
    }

    public void unreferenced() {
	synchronized (this) {
	    unreferenced = true;
	    notifyAll();
	}
    }

    public static void main(String[] args) {

	System.err.println("\nRegression test for bug 4268258\n");

	/*
	 * Set properties to tweak DGC behavior so that this test will execute
	 * quickly: set the granted lease duration to 10 seconds, the interval
	 * that leases are checked to 3 seconds.
	 */
	System.setProperty("java.rmi.dgc.leaseValue", "10000");
	System.setProperty("sun.rmi.dgc.checkInterval", "3000");

	/*
	 * Make idle connections time out almost instantly (0.1 seconds) so
	 * that the DGC implementation will have to make a new connection for
	 * each dirty call, thus going through the socket factory, where we
	 * can easily cause the operation to fail.
	 */
	System.setProperty("sun.rmi.transport.connectionTimeout", "100");

	RetryDirtyCalls impl = new RetryDirtyCalls();

	try {
	    TestSF sf = new TestSF();
	    RMISocketFactory.setSocketFactory(sf);

	    /*
	     * The stub returned by UnicastRemoteObject.exportObject() does
	     * not participate in DGC, but it does allow us to invoke a method
	     * on the remote object through RMI.  Therefore, we invoke the
	     * getSelf() method through RMI, which returns an equivalent stub
	     * that does participate in DGC.
	     */
	    Self stub = (Self) UnicastRemoteObject.exportObject(impl);
	    Self dgcStub = stub.getSelf();
	    stub = null;		// in case 4114579 has been fixed

	    /*
	     * Set the socket factory to cause 3 connections attempts in a row
	     * to fail before allowing a connection to succeed, expecting the
	     * client-side DGC implementation to make at least four attempts.
	     */
	    final int FLAKE_FACTOR = 3;
	    sf.setFlakeFactor(FLAKE_FACTOR);

	    long deadline = System.currentTimeMillis() + TIMEOUT;
	    boolean unreferenced;

	    synchronized (impl) {
		while (!(unreferenced = impl.unreferenced)) {
		    long timeToWait = deadline - System.currentTimeMillis();
		    if (timeToWait > 0) {
			impl.wait(timeToWait);
		    } else {
			break;
		    }
		}
	    }

	    if (unreferenced) {
		throw new RuntimeException("remote object unreferenced");
	    }

	    int createCount = sf.getCreateCount();
	    if (createCount == 0) {
		throw new RuntimeException("test socket factory never used");
	    } else if (createCount < (FLAKE_FACTOR + 3)) {
		/*
		 * The unreferenced method was not invoked for some reason,
		 * but the dirty calls were clearly not retried well enough.
		 */
		throw new RuntimeException(
		    "test failed because dirty calls not retried enough, " +
		    "but remote object not unreferenced");
	    }

	    System.err.println(
		"TEST PASSED: remote object not unreferenced");

	} catch (Exception e) {
	    e.printStackTrace();
	    throw new RuntimeException("TEST FAILED: " + e.toString());
	} finally {
	    /*
	     * When all is said and done, try to unexport the remote object
	     * so that the VM has a chance to exit.
	     */
	    try {
		UnicastRemoteObject.unexportObject(impl, true);
	    } catch (Exception e) {
	    }
	}
    }
}

class TestSF extends RMISocketFactory {

    private int flakeFactor = 0;

    private int flakeState = 0;

    private int createCount = 0;

    public synchronized void setFlakeFactor(int newFlakeFactor) {
	flakeFactor = newFlakeFactor;
    }

    public synchronized int getCreateCount() {
	return createCount;
    }

    public synchronized Socket createSocket(String host, int port)
	throws IOException
    {
	createCount++;

	if (++flakeState > flakeFactor) {
	    flakeState = 0;
	}

	if (flakeState == 0) {
	    return new Socket(host, port);
	} else {
	    throw new IOException("random network failure");
	}
    }

    public ServerSocket createServerSocket(int port) throws IOException {
	return new ServerSocket(port);
    }
}