1

我已经实现了一个 RMI 服务器,现在我想使用 JUnit 测试来测试启动和停止该服务器的方法是否正确实现。

这是我的 RMI 服务器的(简化)代码:

public void startServer() {
    try {
        ...
        registry = LocateRegistry.createRegistry(serverPort);
        registry.rebind("foobar", myRemoteObject);
    } catch (Exception e) {
        e.printStackTrace();
    }  
}

public void stopServer() {
    try {
        registry.unbind("foobar");
    } catch (Exception e) {
        e.printStackTrace();
    }
}

现在,我想做一个 JUnit 测试来检查startServer()stopServer()是否正常工作。

因此,我实现了这个简单的 JUnit 测试:

private boolean isRunning(int port) {
    boolean portTaken = false;
    ServerSocket socket = null;
    try {
        socket = new ServerSocket(port);
        // If we have succesfully created the ServerSocket, then it means that the port is available...
    } catch (IOException e) {
        portTaken = true;
        if (socket != null) {
            try {
                socket.close();
            } catch (IOException e2) {
            }
        }
    }
    return portTaken;
}

@Test
public void testStartAndStopServer() {
    MyRMIServer server = new MyRMIServer();
    assertNotNull(server);
    int port = server.getServerPort();
    assertTrue(port != -1);
    // Check if the current server is NOT running.
    assertFalse(isRunning(port));
    // Start the server and then check if it is running.
    server.startServer();
    assertTrue(isRunning(port));
    // Now stop the server...
    server.stopServer();
    assertFalse(isRunning(port));
}

我的问题是最后一个断言无效。如果我在方法中打印堆栈跟踪,则会isRunning()收到错误消息java.net.BindException: Address already in use: JVM_Bind

这意味着该端口仍在使用,所以我猜我stopServer()的不正确。我做错了什么?

请注意,如果我registry.list()在调用我的方法之前和之后使用该方法stopServer(),我会看到:

BEFORE: foobar
AFTER: (nothing)

编辑:

我还修改了stopServer()关于此处解释的解决方案:

public void stopServer() {
    try {
        registry.unbind("foobar");
        UnicastRemoteObject.unexportObject(myRemoteObject, true);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

但这并不能解决我的问题...

4

3 回答 3

1

我遇到了同样的问题并最终修改了我的服务器代码,以便 start 方法基本上看起来像(注意对 catch 的调用getRegistryrebind 之后的调用,如果它已经存在,它将返回注册表):

public void startServer() {
    try {
        ...
        registry = LocateRegistry.createRegistry(serverPort);
    } catch (RemoteException e) {
    }
    LocateRegistry.getRegistry(port);
    registry.rebind("foobar", myRemoteObject);  
}
于 2009-09-13T14:32:16.997 回答
0

首先,我倾向于避免空异常块,即使在“不可能发生”的情况下也是如此。

  try {
            socket.close();
  } catch (IOException e2) {
  }

只是令人难以置信的是那里有一个例外。

我(相当疯狂)的猜测是,您可能有时间问题。也许网络基础设施需要很短的时间才能注意到套接字已关闭。在最终断言之前睡一两秒钟可以解决问题吗?

于 2009-09-11T08:57:34.623 回答
0

我认为您需要取消导出注册表。尝试:

public void stopServer() {
    try {
        registry.unbind("foobar");
        UnicastRemoteObject.unexportObject(registry, true);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

但是,一位消息人士声称,在 JVM 退出之前,您无法释放该端口。我不知道这是否属实(没有尝试过),但取消导出注册表是朝着正确方向迈出的一步。

于 2009-09-13T14:01:04.153 回答