package conformance.rmi; import test.*; import rmi.*; import java.net.*; /** Performs basic tests on the public interface of {@link rmi.Stub}.

These tests are best performed after SkeletonTest.

The tests performed are:

*/ public class StubTest extends Test { /** Test notice. */ public static final String notice = "checking stub public interface"; /** Prerequisites. */ public static final Class[] prerequisites = new Class[] {SkeletonTest.class}; /** Socket address used for the creation of stubs. */ private InetSocketAddress address; /** Dummy skeleton used during the construction of stubs. */ private Skeleton 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)); } } } }