Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java RMI server side threading

Tags:

java

rmi

I'm just getting started with RMI and I'm trying to write a simple program that simulates a train booking system. I have the basics set up - Server, Client, and a Remote object exported. It works fine with one Client connection. However when more than 1 Client connects, the Clients seem to be executing in the same thread. This is the case when I run multiple Clients on the same machine or when I connect a Client from another laptop.

I was under the impression that RMI handled threading on the server side? If not, how do I go about handling multiple Client connections given the code below?

Here are the classes of interest.

Server.....

public class Server {

    public Server() {
        try {
            Booking stub = (Booking) UnicastRemoteObject.exportObject(new BookingProcess(), 0);
            Registry registry = LocateRegistry.getRegistry();
            registry.bind("Booking", stub);
            System.err.println("Server Ready");
        } catch (RemoteException e) {
            System.err.println("Server exception: " + e.toString());
            e.printStackTrace();
        } catch (AlreadyBoundException e) {
            System.err.println("Server exception: " + e.toString());
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Server server = new Server();
    }

}

BookingProcess.....(I've left out the private methods that processInput(String input) uses)

public class BookingProcess implements Booking {

    private static Journey dublinGalway = new Journey("Dublin to Galway");
    private static Journey dublinLimerick = new Journey("Dublin to Limerick");
    private static Journey dublinCork = new Journey("Dublin to Cork");
    private Journey currentJourney;

    private enum State {
        INITIAL, JOURNEYS_DISPLAYED, JOURNEY_CHOSEN, ANOTHER_BOOKING_OFFERED, SOLD_OUT;
    }

    private State currentState = State.INITIAL;

    public synchronized String processInput(String input) {
        String output = "";

        if(currentState == State.INITIAL) {
            if(bookedOut()) {
                output = "Sorry, there are no seats remaining on any route. Get the bus.";
                currentState = State.SOLD_OUT;
            }
            else {
                output = "Please choose a journey to book: " + "1: " + dublinGalway.getDescription() + ", 2: " + dublinLimerick.getDescription() + ", 3: " + dublinCork.getDescription();
                currentState = State.JOURNEYS_DISPLAYED;
            }
        }

        else if(currentState == State.JOURNEYS_DISPLAYED) {
            output = this.processJourneyChoice(input);
        }

        else if(currentState == State.JOURNEY_CHOSEN) {
            output = "Do you wish to confirm this booking? (y/n)";
            if(input.equalsIgnoreCase("y")) {
                if(bookingConfirmed()) {
                    output = "Thank you. Your journey from " + currentJourney.getDescription() + " is confirmed. Hit return to continue.";
                    //currentState = State.ANOTHER_BOOKING_OFFERED;
                }
                else {
                    output = "Sorry, but the last seat on the " + currentJourney.getDescription() + " route has just been booked by another user.";
                    //currentState = State.ANOTHER_BOOKING_OFFERED;
                }
                currentState = State.ANOTHER_BOOKING_OFFERED;
            }
            else if(input.equalsIgnoreCase("n")) {
                output = "You have cancelled this booking. Hit return to continue.";
                currentState = State.ANOTHER_BOOKING_OFFERED;
            }
        }

        else if(currentState == State.ANOTHER_BOOKING_OFFERED) {
            output = "Would you like to make another booking? (y/n)";
            if(input.equalsIgnoreCase("y")) {
                output = "Hit Return to continue.";
                currentState = State.INITIAL;
            }
            else if(input.equalsIgnoreCase("n")){
                output = "Goodbye.";
                try {
                    Thread.currentThread().join(10);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                currentState = State.INITIAL;
            }
        }

        else if(currentState == State.SOLD_OUT) {
            output = "Goodbye.";
        }

        return output;
    }

And finally Client......

public class Client {

    public static void main(String[] args) {
        Client client = new Client();
        client.runClient();
    }

    public void runClient() {

        try {
            BufferedReader consoleInput = new BufferedReader(new InputStreamReader(System.in));
            Registry registry = LocateRegistry.getRegistry("localhost");
            Booking stub = (Booking) registry.lookup("Booking");
            String serverResponse = stub.processInput("begin");
            System.out.println("Server: " + serverResponse);

            while((serverResponse = stub.processInput(consoleInput.readLine())) != null) {
                 System.out.println(serverResponse);
                 if(serverResponse.equals("Goodbye.")) {
                        break;
                 }
            }
        } catch (Exception e) {
            System.err.println("Client exception " + e.toString());
            e.printStackTrace();
        }
    }


}
like image 701
user1061799 Avatar asked Jul 10 '26 09:07

user1061799


2 Answers

As for as RMI server threads, the answer is that it may or may not run in a separate thread. See the documentation here:

http://docs.oracle.com/javase/6/docs/platform/rmi/spec/rmi-arch3.html

3.2 Thread Usage in Remote Method Invocations

A method dispatched by the RMI runtime to a remote object implementation may or may not execute in a separate thread. The RMI runtime makes no guarantees with respect to mapping remote object invocations to threads. Since remote method invocation on the same remote object may execute concurrently, a remote object implementation needs to make sure its implementation is thread-safe.

You can take server side thread dumps and you would see that the RMI TCP Connection threads IDs keep changing, however as @jtahlborn noticed the server side method is synchronized so it would execute serially, not necessarily in a single thread though.

like image 177
vsnyc Avatar answered Jul 14 '26 00:07

vsnyc


Your server side processInput() method is synchronized, so, yes, the calls will be handled serially. what does that have to do with RMI?

UPDATE:

if you want to have separate currentState and currentJourney values for each client session, then you need to use the RMI remote session pattern, see this answer for details.

like image 20
jtahlborn Avatar answered Jul 14 '26 02:07

jtahlborn