You need to store the result of LocateRegistry.createRegistry(),
and unexport that. At present you're trying to unexport a stub.
Question
A RMI server which works fine without the stopServer functionality.
public class HelloServer extends UnicastRemoteObject implements HelloInterface
{
private final static int PORT=1102;
private final String serverName="server";
private Timer timer;
public HelloServer() throws RemoteException
{
timer = new Timer(); //At this line a new Thread will be created
timer.schedule(new StopServerTask(), 5000);
}
@Override
public String serverResponse(String request) throws RemoteException
{
return "Hello"+request;
}
public static void main(String[] args)
{
try
{
HelloServer skeleton=new HelloServer();
System.out.println("Starting server");
skeleton.startServer();
System.out.println("Server started");
}
catch (RemoteException ex)
{
ex.printStackTrace();
}
}
public void startServer()
{
try {
HelloServer skeleton=new HelloServer();
Registry reg=LocateRegistry.createRegistry(PORT);
reg.rebind(serverName, skeleton);
System.out.println("Server is ready");
} catch (RemoteException ex)
{
Logger.getLogger(HelloInterface.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void stopServer()
{
System.out.println("Stopping server");
try {
Registry rmiRegistry = LocateRegistry.getRegistry(PORT);
HelloInterface myService = (HelloInterface) rmiRegistry.lookup(serverName);
rmiRegistry.unbind(serverName);
UnicastRemoteObject.unexportObject(rmiRegistry, true);
} catch (NoSuchObjectException e)
{
e.printStackTrace();
} catch (NotBoundException e)
{
e.printStackTrace();
} catch (RemoteException ex) {
Logger.getLogger(HelloServer.class.getName()).log(Level.SEVERE, null, ex);
}
}
class StopServerTask extends TimerTask
{
@Override
public void run()
{
stopServer();
}
}
}
Whenever stopServer() in invoked exception is thrown at
UnicastRemoteObject.unexportObject(rmiRegistry, true);
Here is the stack Trace
java.rmi.NoSuchObjectException: object not exported
at sun.rmi.transport.ObjectTable.unexportObject(ObjectTable.java:153)
at java.rmi.server.UnicastRemoteObject.unexportObject(UnicastRemoteObject.java:297)
at rmi.HelloServer.stopServer(HelloServer.java:84)
Things are same even when I clean the service object by using
UnicastRemoteObject.unexportObject(myService, true);
Could someone suggest a clean way to stop the server which also releases the port for reuse.
Solution
OTHER TIPS
I implemented a shutdown-service in my rmi-server. If I want to shut it down, I call it with a password. Simple Example:
public interface ShutdownInterface extends Remote {
public void shutdownService(String password) throws RemoteException;
}
The serverside implementation can look something like:
public class ShutdownService extends UnicastRemoteObject implements ShutdownInterface {
private static final long serialVersionUID = 1L;
private boolean doShutdown = false;
public ShutdownService() throws RemoteException {
super();
}
@Override
public void shutdownService(String password) throws RemoteException {
if ("abcde12345".equals(password)) {
System.out.println("shutdown requested.");
this.doShutdown = true;
} else {
System.out.println("wrong pwd for shutdown");
}
}
public boolean isDoShutdown() {
return this.doShutdown;
}
}
Now the server itself keeps a reference to this:
public class BackendServer {
public final static int RMI_PORT = 1974;
private Registry registry = null;
private ShutdownService shutdownService = null;
public BackendServer() throws RemoteException {
registry = LocateRegistry.createRegistry(RMI_PORT);
this.shutdownService = new ShutdownService();
}
public void initialize() throws AccessException, RemoteException, AlreadyBoundException {
shutdownService = new ShutdownService();
registry.bind("ShutdownService", shutdownService);
registry.bind("MyDataService", new MyDataService());
}
public void stop() throws NoSuchObjectException {
System.out.println("stopping rmi server.");
UnicastRemoteObject.unexportObject(registry, true);
System.exit(0);
}
public boolean shouldStop() {
return this.shutdownService.isDoShutdown();
}
public static void main(String args[]) {
try {
BackendServer bs = new BackendServer();
bs.initialize();
System.out.println("Server ready.");
while (!bs.shouldStop()) {
Thread.sleep(1000);
}
bs.stop();
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
}
Of course, this can be realized in a more beautiful way, but this should give you an idea of how to easily implement a shutdown yourself. You can call it from the main client or from a small commandline-tool you code for your server.