package conformance.rmi;
import test.*;
import rmi.*;
import java.net.*;
/** Performs basic tests on the public interface of {@link rmi.Stub}.
skeleton;
/** Server socket used by the listening thread for the connection check. */
private ServerSocket socket;
/** When false
, the connection check test is complete. */
private boolean listening;
/** Initializes the test.
This method creates the listening socket and dummy skeleton used in the
test.
*/
@Override
protected void initialize() throws TestFailed
{
address = new InetSocketAddress(7000);
listening = true;
try
{
socket = new ServerSocket();
}
catch(Exception e)
{
throw new TestFailed("unable to create listening socket", e);
}
// Create a dummy server and the dummy skeleton.
TestServer server = new TestServer();
try
{
skeleton = new Skeleton(TestInterface.class, server);
}
catch(Throwable t)
{
throw new TestFailed("unable to create dummy skeleton", t);
}
}
/** Performs the test. */
@Override
protected void perform() throws TestFailed
{
ensureUnknownHostRejected();
try
{
skeleton.start();
}
catch(Throwable t)
{
throw new TestFailed("unable to start dummy skeleton", t);
}
ensureClassRejected();
ensureNonRemoteInterfaceRejected();
ensureNullPointerExceptions();
ensureLocalMethods();
skeleton.stop();
ensureStubConnects();
}
/** Checks that a stub connects to the server for which it was created.
@throws TestFailed If the stub does not connect.
*/
private void ensureStubConnects() throws TestFailed
{
TestInterface stub;
// Create the stub.
try
{
stub = Stub.create(TestInterface.class, address);
}
catch(Throwable t)
{
throw new TestFailed("unable to create stub for connecting to " +
"test server", t);
}
// Bind the listening socket.
try
{
socket.bind(address);
}
catch(Exception e)
{
throw new TestFailed("unable to bind listening socket to " +
"address", e);
}
// Start the listening thread. The thread will not be able to call wake
// until this function calls wait.
new Thread(new ConnectionCheckThread()).start();
// Attempt to connect to the listening server.
try
{
stub.method(false);
}
catch(RMIException e)
{
return;
}
catch(Throwable t)
{
throw new TestFailed("exception when attempting to connect to " +
"server", t);
}
throw new TestFailed("stub sent no data");
}
/** Stops the dummy skeleton and testing server. */
@Override
protected void clean()
{
skeleton.stop();
try
{
socket.close();
}
catch(Exception e) { }
}
/** Ensures that a stub cannot be created from a skeleton whose address has
not been determined.
This method should be used before the test skeleton is started.
@throws TestFailed If IllegalStateException
is not thrown.
*/
private void ensureUnknownHostRejected() throws TestFailed
{
try
{
TestInterface stub = Stub.create(TestInterface.class, skeleton);
throw new TestFailed("Stub.create(Class, Skeleton) allowed " +
"stub to be created from skeleton with " +
"unassigned address");
}
catch(TestFailed e) { throw e; }
catch(IllegalStateException e) { }
catch(Throwable t)
{
throw new TestFailed("Stub.create(Class, Skeleton) threw " +
"an unexpected exception when given a " +
"skeleton with an unassigned address", t);
}
}
/** Ensures that a Stub
cannot be created from a class rather
than an interface.
@throws TestFailed If a Stub
is created from a class, or if
an unexpected exception occurs.
*/
private void ensureClassRejected() throws TestFailed
{
try
{
Object stub = Stub.create(Object.class, address);
throw new TestFailed("Stub.create(Class, InetSocketAddress) " +
"has accepted a class");
}
catch(TestFailed e) { throw e; }
catch(Error e) { }
catch(Throwable t)
{
throw new TestFailed("Stub.create(Class, InetSocketAddress) " +
"constructor threw an unexpected exception " +
"when given a class", t);
}
}
/** Ensures that a Stub
cannot be created from a non-remote
interface.
@throws TestFailed If a Stub
is created from a non-remote
interface, or if an unexpected exception occurs.
*/
private void ensureNonRemoteInterfaceRejected() throws TestFailed
{
try
{
BadInterface stub = Stub.create(BadInterface.class, address);
throw new TestFailed("Stub.create(Class, InetSocketAddress) " +
"has accepted a non-remote interface");
}
catch(TestFailed e) { throw e; }
catch(Error e) { }
catch(Throwable t)
{
throw new TestFailed("Stub.create(Class, InetSocketAddress) " +
"constructor threw an unexpected exception " +
"when given a non-remote interface", t);
}
}
/** Ensures that both Stub.create
methods throw
NullPointerException
when given null
for any
parameters.
@throws TestFailed If null
is given as a parameter but the
correct exception is not thrown.
*/
private void ensureNullPointerExceptions() throws TestFailed
{
// Make sure that null for the first argument is rejected.
try
{
TestInterface stub = Stub.create(null, skeleton);
throw new TestFailed("Stub.create(Class, Skeleton) " +
"accepted null for first argument");
}
catch(TestFailed e) { throw e; }
catch(NullPointerException e) { }
catch(Throwable t)
{
throw new TestFailed("Stub.create(Class, Skeleton) threw " +
"an unexpected exception when given null " +
"for first argument", t);
}
try
{
TestInterface stub = Stub.create(null, skeleton, "127.0.0.1");
throw new TestFailed("Stub.create(Class, Skeleton, String) " +
"accepted null for first argument");
}
catch(TestFailed e) { throw e; }
catch(NullPointerException e) { }
catch(Throwable t)
{
throw new TestFailed("Stub.create(Class, Skeleton, String) " +
"thew an unexpected exception when given " +
"null for first argument", t);
}
try
{
TestInterface stub = Stub.create(null, address);
throw new TestFailed("Stub.create(Class, InetSocketAddress) " +
"accepted null for first argument");
}
catch(TestFailed e) { throw e; }
catch(NullPointerException e) { }
catch(Throwable t)
{
throw new TestFailed("Stub.create(Class, InetSocketAddress) " +
"threw an unexpected exception when given " +
"null for first argument", t);
}
// Make sure that null for the second argument is rejected.
try
{
TestInterface stub =
Stub.create(TestInterface.class, (Skeleton)null);
throw new TestFailed("Stub.create(Class, Skeleton) " +
"accepted null for second argument");
}
catch(TestFailed e) { throw e; }
catch(NullPointerException e) { }
catch(Throwable t)
{
throw new TestFailed("Stub.create(Class, Skeleton) threw " +
"an unexpected exception when given null " +
"for second argument", t);
}
try
{
TestInterface stub =
Stub.create(TestInterface.class, (Skeleton)null,
"127.0.0.1");
throw new TestFailed("Stub.create(Class, Skeleton, String) " +
"accepted null for second argument");
}
catch(TestFailed e) { throw e; }
catch(NullPointerException e) { }
catch(Throwable t)
{
throw new TestFailed("Stub.create(Class, Skeleton, String) " +
"threw an unexpected exception when given " +
"null for second argument", t);
}
try
{
TestInterface stub =
Stub.create(TestInterface.class, (InetSocketAddress)null);
throw new TestFailed("Stub.create(Class, InetSocketAddress) " +
"accepted null for second argument");
}
catch(TestFailed e) { throw e; }
catch(NullPointerException e) { }
catch(Throwable t)
{
throw new TestFailed("Stub.create(Class, InetSocketAddress) " +
"threw an unexpected exception when given " +
"null for second argument", t);
}
// Make sure that the three-argument form of create rejects null for the
// third argument.
try
{
TestInterface stub =
Stub.create(TestInterface.class, skeleton, null);
throw new TestFailed("Stub.create(Class, Skeleton, String) " +
"accepted null for third argument");
}
catch(TestFailed e) { throw e; }
catch(NullPointerException e) { }
catch(Throwable t)
{
throw new TestFailed("Stub.create(Class, Skeleton, String) " +
"threw an unexpected exception when given " +
"null for third argument", t);
}
}
/** Ensures that stubs for the same skeleton are equal and have the same
hash code, and stubs have a string representation.
@throws TestFailed If the test fails.
*/
private void ensureLocalMethods() throws TestFailed
{
// Create two stubs for the same skeleton.
TestInterface stub1;
TestInterface stub2;
try
{
stub1 = Stub.create(TestInterface.class, skeleton);
stub2 = Stub.create(TestInterface.class, skeleton);
}
catch(Throwable t)
{
throw new TestFailed("unable to create stub", t);
}
// Create a third stub for a different skeleton. The port is in the
// reserved port range, so it is not one of the ports that the system
// may automatically assign to the other skeletons (and therefore the
// other two stubs).
TestInterface stub3 =
Stub.create(TestInterface.class, new InetSocketAddress(80));
// Check that stubs are not equal to null.
try
{
if(stub1.equals(null))
throw new TestFailed("stub is reported as equal to null");
}
catch(TestFailed e) { throw e; }
catch(Throwable t)
{
throw new TestFailed("equals threw an unexpected exception when " +
"comparing a stub to null", t);
}
// Check that the first two stubs are equal.
try
{
if(!stub1.equals(stub2))
{
throw new TestFailed("stubs for the same skeleton are not " +
"equal");
}
}
catch(TestFailed e) { throw e; }
catch(Throwable t)
{
throw new TestFailed("equals threw an unexpected exception when " +
"comparing equal stubs", t);
}
// Check that the first stub is different from the last.
try
{
if(stub1.equals(stub3))
throw new TestFailed("stubs for different skeletons are equal");
}
catch(TestFailed e) { throw e; }
catch(Throwable t)
{
throw new TestFailed("equals threw an unexpected exception when " +
"comparing unequal stubs", t);
}
// Check that the first two stubs have the same hash code.
try
{
if(stub1.hashCode() != stub2.hashCode())
throw new TestFailed("equal stubs have different hash codes");
}
catch(TestFailed e) { throw e; }
catch(Throwable t)
{
throw new TestFailed("hashCode threw an unexpected exception", t);
}
// Check that the first and third stubs have different hash codes.
try
{
if(stub1.hashCode() == stub3.hashCode())
throw new TestFailed("unequal stubs have the same hash code");
}
catch(TestFailed e) { throw e; }
catch(Throwable t)
{
throw new TestFailed("hashCode threw an unexpected exception", t);
}
// Check that the toString method is implemented.
try
{
stub1.toString();
}
catch(Throwable t)
{
throw new TestFailed("toString threw an unexpected exception", t);
}
}
/** Thread listening for a connection. */
private class ConnectionCheckThread implements Runnable
{
/** Accepts one connection at the server socket, then closes the
connected socket. */
@Override
public void run()
{
try
{
Socket connected = socket.accept();
try
{
connected.close();
}
catch(Exception e) { }
}
catch(Exception e)
{
// An exception may be generated due to a genuine error, or
// because clean has already been called. If clean has already
// been called, this call to failure will have no effect.
failure(new TestFailed("caught an exception while listening " +
"for a connection", e));
}
}
}
}