Mockarutan
Mockarutan

Reputation: 442

Returning class instance via RMI call

I'm trying to return a normal class via a RMI call. My server holds a instance of a class called GameState that i want to perform actions on via it's methods, from a client application. So the RMI works fine if a just return a int or something, but when a try to return GameState, which is a class defined inside the GameServer java file, the following error occurs (game state is declared neither public, protected or private):

Exception in thread "main" java.lang.IllegalAccessError: tried to access class GameState from class $Proxy0 at $Proxy0.getGameState(Unknown Source) at GameClient.login(GameClient.java:204) at GameClient.main(GameClient.java:168)

So, i guess the client application knows how GameState looks, but dont have any access to it?

I have tried to make GameState a public class in it's own file, but then the different connecting client applications get each their own GameState, so it's seems like that dont get it from the server.

Here are some code that i think is relevant:

The remote interface:

import java.rmi.*;

public interface ServerInterface extends Remote
{
    public  GameState getGameState()  throws RemoteException;
}

Some if the server code:

public class GameServer extends UnicastRemoteObject implements ServerInterface {
    /**
     * 
     */
    private static final long serialVersionUID = -6633456258968168102L;

    private final static int DEFAULT_NAMING_PORT = 9955; // TODO: IMPORTANT - change this to a group-specific number,
    // e.g., 2000 + group number. The number should be the same
    // as in GameClient.java.

    private final GameState m_state;

    public static void main(String[] args) {

            //the variables: port and host etc it configurated here, but has nothing to do with the RMI problem.

        try {
            GameServer instance = new GameServer(players);
            System.out.print("Setting up registry on "+host+":"+port+"  ... ");

            //Set up an unrestricted security manager.
            if (System.getSecurityManager() == null) {
                // Set security manager to an instance of a dynamically created
                // subclass of RMISecurityManager with the checkPermission() method overloaded
                System.setSecurityManager(
                        new RMISecurityManager() {
                            @Override
                            public void checkPermission(Permission permission) {
                            }
                        }
                );
            }

            // Create a registry for binding names (name server)
            Registry naming = LocateRegistry.createRegistry(port);
            System.out.println("done.");

            String rmiObjectName = "GeschenktServer";
            System.out.print("Binding name "+rmiObjectName+" ... ");
            naming.rebind(rmiObjectName, instance);
            System.out.println("done.");
        } catch(RemoteException e) {
                System.err.println("Could not start server: "+e);
                System.exit(-1);
            }
        }

  //the rest of the server code....

  //the GameState declared in the same file

class GameState implements java.io.Serializable {

    private static final long serialVersionUID = 545671487061859760L;

//the rest of the Game state code.

Here is some of the client code:

private void login() {
        try {
            System.out.println("Connecting to server on host "+m_host+".");

            // Set up an unrestricted security manager. In the server we trust.
            // See GameServer.java for code explanation.
            if (System.getSecurityManager() == null) {
                System.setSecurityManager(
                        new RMISecurityManager() {
                            @Override
                            public void checkPermission(Permission permission) {
                            }
                        }
                );
            }

            System.out.print("Locating registry on "+m_host+":"+m_port+"  ... ");
            Registry naming = LocateRegistry.getRegistry(m_host, m_port);
            System.out.println("done.");
            String name = "GeschenktServer";
            System.out.print("Looking up name "+name+" ... ");
            m_server = (ServerInterface) naming.lookup(name);
            System.out.println("done.");

            // TODO: Connect the player, i.e., register the player with the server.
            // Make sure that the player cannot register if there are already enough players.

            m_Id = m_server.getGameState().loginPlayer(m_name);  //this line is causing the error...

            if(m_Id < 0)
                System.exit(0);

            System.out.println("Server connection successful.");        
            m_window = new GameWindow(m_server, m_name, m_Id);
            m_window.run();
        } catch (Exception e) {
            System.out.println("Connection failed - "+e);
            System.exit(1);
        }
    }   
}

I am using eclipse to do all this, and based on what i have red about RMI in eclipse, rmic and that stuff is not needed anymore, i'm i right?

So anyone with any idea?

Thanks in advance!

Upvotes: 1

Views: 1860

Answers (3)

bestsss
bestsss

Reputation: 12066

The IllegalAccessError reason is simple:

    GameState is NOT public

However, there is a larger issue:

you do understand that loginPlayer will not do what you like it to... The GameState is a copy of the original state. You want GameState to be Remote not serializable, so you can execute the operation on the server, not each client to get a useless copy of.

Upvotes: 0

user207421
user207421

Reputation: 311039

try to return GameState, which is a class defined inside the GameServer java file, the following error occurs (game state is declared neither public, protected or private)

This is the problem. Only the GameServer class and classes in the same package can create instances of GameState. Your RMI proxy object (stub) Make it a public class in its own file.

I have tried to make GameState a public class in it's own file, but then the different connecting client applications get each their own GameState, so it's seems like that dont get it from the server

That's correct. It is serialized to each client. If you want to share a single GameState and have it remain at the server, it has to be an exported remote object itself, with a remote interface called GameState.

Upvotes: 0

joergl
joergl

Reputation: 2866

I don't think this is a permission problem. I cannot tell for sure from the code above, but I would assume it is a codebase problem. Did you configure the codebase also on client-side?

To deserialize the GameState class, the client needs to be able to load the class definition. This definition is located in the Server implementation and not the interface. Normally, the Server implementation should not be compiled to the client's classpath, only the interface should. I am not entirely sure, as in your solution the interface seems to have a dependency on the implementation due to the GameState which is not a good idea btw. Anyways, try adding a codebase configuration to your VM-args. Assuming you execute everything on localhost, it should look like this:

-Djava.rmi.server.codebase=file:${workspace_loc}/PROJECT-NAME/bin/

Where ${workspace_loc} is the absolute path to your workspace and PROJECT-NAME is the name of the server project. Eclipse will resolve ${workspace_loc} automatically, so you only need to set your PROJECT-NAME

As a side note: If you implement it that way, the GameState object is transmitted to the client-side and is executed on the client, having no effect whatsoever on the execution of the server. Is this really what you want? If you want the GameState instance to execute on the server-side, GameState also needs to implement Remote, not Serializable, and you need to export it when transmitting its stub to the client.

Finally, as you correctly stated, you do not need to use rmic since Java 1.5

Upvotes: 0

Related Questions