通过RMI调用返回类实例

时间:2012-02-09 13:32:20

标签: java permissions rmi

我正在尝试通过RMI调用返回普通类。我的服务器拥有一个名为GameState的类的实例,我希望通过它的方法从客户端应用程序执行操作。因此,如果只返回一个int或者其他东西,RMI工作正常,但是当尝试返回GameState时,GameServer java文件中定义的类,会发生以下错误(游戏状态声明既不公开,也不是私有):

线程“main”中的异常java.lang.IllegalAccessError:尝试从类$ Proxy0访问类GameState     在$ Proxy0.getGameState(未知来源)     在GameClient.login(GameClient.java:204)     在GameClient.main(GameClient.java:168)

所以,我猜客户端应用程序知道GameState的外观,但是没有任何访问权限吗?

我试图让GameState成为自己文件中的公共类,但是不同的连接客户端应用程序会获得每个自己的GameState,所以看起来好像不是从服务器上获取它。

以下是我认为相关的一些代码:

远程接口:

import java.rmi.*;

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

一些服务器代码:

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.

以下是一些客户端代码:

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);
        }
    }   
}

我正在使用eclipse来完成所有这些工作,并且基于我在eclipse中对RMI的了解,rmic并且不再需要那些东西了,我是对的吗?

所有有想法的人?

提前致谢!

3 个答案:

答案 0 :(得分:0)

我不认为这是一个许可问题。我无法从上面的代码中确定,但我认为它是一个代码库问题。您是否也在客户端配置了代码库?

要反序列化GameState类,客户端需要能够加载类定义。此定义位于Server实现中,而不是接口。通常,不应将Server实现编译到客户端的类路径,只应该接口应该。我不完全确定,因为在你的解决方案中,界面似乎依赖于实现,因为GameState这不是一个好主意btw。无论如何,尝试向VM-args添加代码库配置。假设您在localhost上执行所有操作,它应该如下所示:

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

其中${workspace_loc}是工作区的绝对路径,PROJECT-NAME是服务器项目的名称。 Eclipse将自动解析${workspace_loc},因此您只需设置PROJECT-NAME

即可

作为旁注:如果以这种方式实现,GameState对象将传输到客户端并在客户端执行,对服务器的执行没有任何影响。这真的是你想要的吗?如果您希望GameState实例在服务器端执行,GameState也需要实现Remote,而不是Serializable,并且您需要在传输其存根时导出它给客户。

最后,正如您所说,从Java 1.5开始,您不需要使用rmic

答案 1 :(得分:0)

  

尝试返回GameState,这是一个在其中定义的类   GameServer java文件,发生以下错误(游戏状态为   宣称既不公开,也不保护或私有)

这就是问题所在。只有GameServer类和同一个包中的类可以创建GameState的实例。您的RMI代理对象(存根)使其成为自己文件中的公共类。

  

我试图让GameState成为自己文件中的公共类,但是   然后不同的连接客户端应用程序各自拥有自己的   GameState,所以它似乎从服务器上得不到它

这是对的。它被序列化到每个客户端。如果你想共享一个GameState并让它保留在服务器上,它必须是一个导出的远程对象本身,带有一个名为GameState的远程接口

答案 2 :(得分:0)

IllegalAccessError原因很简单:

    GameState不公开

但是,有一个更大的问题:

你明白loginPlayer不会做你喜欢的事情...... GameState是原始状态的副本。您希望GameState为Remote不可序列化,因此您可以在服务器上执行操作,而不是每个客户端都可以获得无用的副本。